/***************************************************************************
 *   Copyright (C) 2007 - 2009 by Andreas Theofilu                         *
 *   andreas@theosys.at                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation version 3 of the License.                *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "config.h"
#include "managefile.h"
#include "sportwatcherwidget.h"
#include "settingswidget.h"
#include "progresswidget.h"
#include "wmsbase.h"
#include "coordinateswidget.h"
#include "shapewidget.h"
#include <string.h>

#include <iostream>
#include <kfiledialog.h>
#include <kmessagebox.h>
#include <KConfig>
#include <klocale.h>
#include <k3listview.h>
#include <kaboutdata.h>
#include <kaboutapplicationdialog.h>
#include <kinputdialog.h>
#include <kglobalsettings.h>
#include <kcombobox.h>
#include <qapplication.h>
#include <qstring.h>
#include <qdatetime.h>
#include <qtoolbutton.h>
#include <qcursor.h>
#include <qregexp.h>
#include <QMouseEvent>
#include <QRect>
#include <QRegion>
#include <KIcon>
#include <QPrintDialog>
#include <QPrinter>

#if defined HAVE_GDAL
   #include <gdal/ogr_spatialref.h>
   #include <gdal/ogrsf_frmts.h>
   #include <gdal/gdalwarper.h>
   #include <gdal/ogrsf_frmts.h>
   #include "GDALError.h"
#endif

#include "garmin.h"
#include "transform.h"
#include "import.h"

#if defined HAVE_MAPNIK
   #include "render.h"
#endif

using std::cout;
using std::cerr;
using std::clog;
using std::endl;


typedef struct
{
	double lon;
	double lat;
} posn_type;

#if defined HAVE_GDAL
typedef struct ERRMSG
{
	char msg[4096];
	ERRMSG *next;
}ERRMSG;

ERRMSG *firstError;
#endif

sportwatcherWidget::sportwatcherWidget(QWidget *parent)
{
	ui_sportwatcherWidgetBase.setupUi(this);

#if defined HAVE_GDAL
	mFactor = 10;		// Factor to calculate square pixels
	firstError = 0;
#endif
	mama = parent;
	gmn = 0;
	min_hr = max_hr = avg_hr = 0;
	min_height = max_height = 0.0;
	max_time = 0;
	zfactor = 0;
	mapLap = 0;
	mapPan = QRect(0, 0, 0, 0);
	stateHand = stateFlag = stateGlas = false;
	oldTransX = oldTransY = 0.0;
	lmbPressed = 0;
	DIRTY = true;
	curTab = ui_sportwatcherWidgetBase.tabView->currentIndex();
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
	ActivePrint = false;	// true if we are printing on paper
	kl = new KLocale(QString("kdesktop"), 0);

	// Load the config parameters
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
	KConfigGroup ic (&cfg, "SportWatcher");
	lower1 = ic.readEntry("lower1", 0);
	lower2 = ic.readEntry("lower2", 0);
	lower3 = ic.readEntry("lower3", 0);
	upper1 = ic.readEntry("upper1", 0);
	upper2 = ic.readEntry("upper2", 0);
	upper3 = ic.readEntry("upper3", 0);
	MaxHr = ic.readEntry("maxHr", 180);
	restHr = ic.readEntry("restHr", 60);
	vo2max = ic.readEntry("vo2max", 50);
	weight = ic.readEntry("weight", 70);
	sampleTime = ic.readEntry("seconds", 15);
	Serial = ic.readEntry("Serial", false);
	Forerunner = ic.readEntry("Forerunner", false);
	Contour = ic.readEntry("Contour", false);
	Device = ic.readEntry("Device", "/dev/ttyUSB0");
	Data = ic.readEntry("Data", QDir::home().absolutePath() + "/.sportwatcher");
	HRM = ic.readEntry("HRM", QDir::home().absolutePath() + "/polar");
	MAP = ic.readEntry("MAP", QDir::home().absolutePath() + "/.sportwatcher/track.wms");
	Units = ic.readEntry("Units", 0);
	MapType = ic.readEntry("MapType", 7);
#ifdef HAVE_GDAL
	// set our own error handler here
	CPLSetErrorHandler((CPLErrorHandler)spwErrorHandler);
#endif

}

sportwatcherWidget::~sportwatcherWidget()
{
	destroy();
	delete kl;
#ifdef HAVE_GDAL
	// destroy the error handler here
	CPLSetErrorHandler(NULL);
#endif
}

void sportwatcherWidget::destroy()
{
	if (gmn)
	   garmin_free_data (gmn);

	gmn = 0;
	oldTransX = oldTransY = 0.0;
}

void sportwatcherWidget::Initialize()
{
	// Set some widget settings
	ui_sportwatcherWidgetBase.btHand->setIcon(KIcon("hand"));
	ui_sportwatcherWidgetBase.btGlas->setIcon(KIcon("glas"));
	ui_sportwatcherWidgetBase.btGlasMinus->setIcon(KIcon("glas_minus"));
	ui_sportwatcherWidgetBase.btGlasPlus->setIcon(KIcon("glas_plus"));
	ui_sportwatcherWidgetBase.btFlag->setIcon(KIcon("flag"));
	ui_sportwatcherWidgetBase.btFullscreen->setIcon(KIcon("fullscreen"));

	if (curTab != 0)
	{
	   ui_sportwatcherWidgetBase.tabView->setCurrentIndex(0);
	   curTab = ui_sportwatcherWidgetBase.tabView->currentIndex();
	}

//	btHand->setToggleButton(true);
//	btGlas->setToggleButton(true);
	// Fill the activities
	getActivities();
#if defined HAVE_GDAL
	// Initialize the GDAL
	GDALAllRegister();
	poDataset = 0;
#endif
}

QTreeWidgetItem *sportwatcherWidget::findElement(QTreeWidget *wdg, const QString &val)
{
QTreeWidgetItem *item, *child, *subchild;

	if (!wdg)
	   return 0;

	for (int a = 0; a < wdg->topLevelItemCount(); a++)
	{
	   if (!(item = wdg->topLevelItem(a)))
	      continue;

	   for (int j = 0; j < item->childCount(); j++)
	   {
	      if (!(child = item->child(j)))
		 continue;

	      if (child->data(0, Qt::UserRole).toString() == val)
		 return child;

	      for (int i = 0; i < child->childCount(); i++)
	      {
		 if (!(subchild = child->child(i)))
		    continue;

		 if (subchild->data(0, Qt::UserRole).toString() == val)
		    return subchild;
	      }
	   }
	}

	return 0;
}

/*
 * Search for a directory named .sportwatcher in the home directory of
 * the user and search for *.gmn files. Open the files and read the header
 * to find the basic data of activities. Then add the information into
 * the activities KListView.
 */
void sportwatcherWidget::getActivities()
{
QString path, txt;
QDir mdir, dir = QDir::home();
QStringList years, months;
QTreeWidgetItem *running, *biking, *other, *year_run, *year_bike, *year_other;
QTreeWidgetItem *el;
QList<QTreeWidgetItem *> item;
int i, j;
int y_run, y_bike, y_other;
RUN_NODE *rn;
LAP *lap;

	if (Data.isEmpty())
	{
	   path = dir.homePath();
	   path.append("/.sportwatcher");
	}
	else
	   path = Data;

	dir.setPath(path);
	dir.refresh();

	if (!dir.exists())
	{
	   dir.mkdir(path);
	   return;
	}

	destroy();

	if (!ui_sportwatcherWidgetBase.liActivities)
	{
	   KMessageBox::error(this, i18n("Error initializing some widgets of main window!"));
	   return;
	}

	ui_sportwatcherWidgetBase.liActivities->clear();
	ui_sportwatcherWidgetBase.liActivities->setSortingEnabled(false);
	ui_sportwatcherWidgetBase.liActivities->setColumnCount(1);

	running = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
	biking = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
	other = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);

	if (!other || !biking || !running)
	{
	   KMessageBox::error(this, i18n("Not enough memory to initilize application!"));
	   return;
	}

	running->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
	biking->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
	other->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);

	running->setText(0, i18n("Running"));
	biking->setText(0, i18n("Biking"));
	other->setText(0, i18n("Others"));

	KIcon fld(QString("history"));
	running->setIcon(0, fld);
	biking->setIcon(0, fld);
	other->setIcon(0, fld);

	running->setStatusTip(0, i18n("\"Running\" activities."));
	biking->setStatusTip(0, i18n("\"Biking\" activities."));
	other->setStatusTip(0, i18n("\"Other\" and \"Multisport\" activities."));

	dir.cd(path);
	dir.setFilter(QDir::Dirs | QDir::NoSymLinks);
	dir.setSorting(QDir::Name);
	dir.refresh();
	QFileInfoList list = dir.entryInfoList();

	if (!list.size())
	   return;

	for (i = 0; i < list.size(); i++)		// Years
	{
	QFileInfo fileInfo = list.at(i);

	   if (fileInfo.fileName() == QString(".") || fileInfo.fileName() == QString(".."))
	      continue;

	   years += fileInfo.absoluteFilePath();
	}

	for (i = 0; i < years.size(); ++i)
	{
	   if (months.size() > 0)
	      months.clear();

	   dir.setPath(years.at(i));
	   dir.refresh();
	   list = dir.entryInfoList();

	   if (!list.size())
	      continue;

	   for (j = 0; j < list.size(); j++)		// Months
	   {
	   QFileInfo fileInfo = list.at(j);

	      if (fileInfo.fileName() == QString(".") || fileInfo.fileName() == QString(".."))
		 continue;

	      months += fileInfo.absoluteFilePath();
	   }

	   for (j = 0; j < months.size(); ++j)
	   {
	      mdir.setPath(months.at(j));
	      mdir.cd(months.at(j));
	      mdir.setFilter(QDir::Files | QDir::NoSymLinks);
	      mdir.setSorting(QDir::Name);
	      mdir.setNameFilters(QStringList("*.gmn"));
	      mdir.refresh();
	      list = mdir.entryInfoList();

	      if (!list.size())
		 continue;

	      for (int a = 0; a < list.size(); a++)		// Files
	      {
	      QFileInfo fileInfo = list.at(a);

		 files += fileInfo.absoluteFilePath();
	      }
	   }
	}

	y_run = y_bike = y_other = 0;
	year_run = year_bike = year_other = 0;
	// Open every file and read its head
	for (i = 0; i < files.size(); ++i)
	{
	QTreeWidgetItem *yr, *yb, *yo;

	   if (findElement(ui_sportwatcherWidgetBase.liActivities, files.at(i)))
	      continue;

	   spw.destroy();

	   if (spw.setFileName(files.at(i).toAscii().constData()) == -1)
	      return;

	   if (gmn)
	   {
	      garmin_free_data (gmn);
	      gmn = 0;
	   }

	   if (!(gmn = spw.readFile()))
	      continue;

	   ds.destroy();
	   ds.garmin_print_data(gmn);
	   rn = ds.getRunNode();

	   if (!rn)
	      return;

	   lap = ds.getLap(rn->run->first_lap_index);

	   if (!lap)
	      return;

	   const QDateTime *qt = garmin_dtime (lap->start_time);
	   // By default set the name of the session to the date and time
	   // it was recorded.
	   QString idx = kl->formatDateTime (*qt, KLocale::ShortDate, true);
	   QString stTip = kl->formatDateTime (*qt, KLocale::LongDate, true);

	   // If we have a custom name for this session, set it.
	   if (strlen (rn->run->workout.name) > 1 && strlen (rn->run->workout.name) < 16 && isalpha (rn->run->workout.name[0]))
	      idx = QString (rn->run->workout.name);

	   switch (rn->run->sport_type)
	   {
	      case D1000_running:
		 yr = findElement(ui_sportwatcherWidgetBase.liActivities, QString("run_%1").arg(qt->date().year()));

		 if (!yr && qt->date().year() != y_run)
		 {
		    year_run = new QTreeWidgetItem(running, running, QTreeWidgetItem::Type);
		    y_run = qt->date().year();
		    year_run->setText(0, QString("%1").arg(y_run));
		    year_run->setData(0, Qt::UserRole, QString("run_%1").arg(y_run));
		    year_run->setIcon(0, KIcon(QString("today")));
		    year_run->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
		 }
		 else
		    year_run = yr;

		 el = new QTreeWidgetItem(year_run, year_run, QTreeWidgetItem::Type);
		 el->setText(0, idx);
		 el->setData(0, Qt::UserRole, files.at(i));
		 el->setIcon(0, KIcon(QString("spw-running")));
		 el->setStatusTip(0, stTip);
		 el->setToolTip(0, stTip);
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
	      break;

	      case D1000_biking:
		 yb = findElement(ui_sportwatcherWidgetBase.liActivities, QString("bike_%1").arg(qt->date().year()));

		 if (!yb && qt->date().year() != y_bike)
		 {
		    year_bike = new QTreeWidgetItem(biking, biking, QTreeWidgetItem::Type);
		    y_bike = qt->date().year();
		    year_bike->setText(0, QString("%1").arg(y_bike));
		    year_bike->setData(0, Qt::UserRole, QString("bike_%1").arg(y_bike));
		    year_bike->setIcon(0, KIcon(QString("today")));
		    year_bike->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
		 }
		 else
		    year_bike = yb;

		 el = new QTreeWidgetItem(year_bike, year_bike, QTreeWidgetItem::Type);
		 el->setText(0, idx);
		 el->setData(0, Qt::UserRole, files.at(i));
		 el->setStatusTip(0, stTip);
		 el->setToolTip(0, stTip);
		 el->setIcon(0, KIcon(QString("bike")));
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
	      break;

	      case D1000_other:
	      default:
		 yo = findElement(ui_sportwatcherWidgetBase.liActivities, QString("other_%1").arg(qt->date().year()));

		 if (!yo && qt->date().year() != y_other)
		 {
		    year_other = new QTreeWidgetItem(other, other, QTreeWidgetItem::Type);
		    y_other = qt->date().year();
		    year_other->setText(0, QString("%1").arg(y_other));
		    year_other->setData(0, Qt::UserRole, QString("other_%1").arg(y_other));
		    year_other->setIcon(0, KIcon(QString("today")));
		    year_other->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
		 }
		 else
		    year_other = yo;

		 el = new QTreeWidgetItem(year_other, year_other, QTreeWidgetItem::Type);
		 el->setText(0, idx);
		 el->setData(0, Qt::UserRole, files.at(i));
		 el->setStatusTip(0, stTip);
		 el->setToolTip(0, stTip);
		 el->setIcon(0, KIcon(QString("other")));
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
	   }

	   delete qt;
	}

	ui_sportwatcherWidgetBase.liActivities->setItemExpanded(running, true);

	if (year_run)
	   ui_sportwatcherWidgetBase.liActivities->setItemExpanded(year_run, true);

	if (gmn)
	   garmin_free_data (gmn);

	gmn = 0;
}

/*$SPECIALIZATION$*/
void sportwatcherWidget::btFullscreenSlot()
{
	oldTransX = oldTransY = 0.0;
	mapPan.setCoords(0, 0, 0, 0);
	DIRTY = true;
	showTrack(0);
	DIRTY = false;
}

void sportwatcherWidget::btGlasMinusSlot()
{
bool sh = stateHand;

	stateHand = false;
	DIRTY = true;
	showTrack(zfactor - 1000);
	DIRTY = false;
	stateHand = sh;
}

void sportwatcherWidget::btGlasPlusSlot()
{
bool sh = stateHand;

	stateHand = false;
	DIRTY = true;
	showTrack(zfactor + 1000);
	DIRTY = false;
	stateHand = sh;
}

void sportwatcherWidget::btHandSlot()
{
QCursor cs;

	if (stateGlas)
	{
	   stateGlas = false;
	   ui_sportwatcherWidgetBase.btGlas->toggle();
	}
	
	stateHand = (stateHand) ? false : true;

	if (stateHand)
	   cs.setShape(Qt::PointingHandCursor);
	else
	   cs.setShape(Qt::ArrowCursor);

	ui_sportwatcherWidgetBase.imgMap->setCursor(cs);
}

void sportwatcherWidget::btGlasSlot()
{
QCursor cs;

	if (stateHand)
	{
	   stateHand = false;
	   ui_sportwatcherWidgetBase.btHand->toggle();
	}

	stateGlas = (stateGlas) ? false : true;

	if (stateGlas)
	   cs.setShape(Qt::ForbiddenCursor);
	else
	   cs.setShape(Qt::ArrowCursor);

	ui_sportwatcherWidgetBase.imgMap->setCursor(cs);
}

void sportwatcherWidget::btFlagSlot()
{
}

void sportwatcherWidget::liLapsSlot(Q3ListViewItem *item)
{
QString sl;
int l;
int idx;
RUN_NODE *rn;
LAP *lap;

	if (!item)
	   return;

	DIRTY = true;
	sl = item->text(0).mid(4, 3);
	l = sl.toInt();

	if (l <= 0)
	{
	   // Show the whole map and track
	   showTrack(zfactor, mapPan, 0);
	   // Don't mark any lap. Just display normal.
	   showCurves(0);
	   return;
	}

	rn = ds.getRunNode();
	idx = rn->run->first_lap_index;
	lap = ds.getLap(idx + l - 1);
	// Show the part of the track, corresponding to the selected lap
	showTrack(zfactor, mapPan, lap);
	// Mark the selected lap
	showCurves(lap);
	DIRTY = false;
}

void sportwatcherWidget::liActivitiesSlot(QTreeWidgetItem *item, int)
{
	if (!item)
	   return;

	spw.destroy();

	if (spw.setFileName(item->data(0, Qt::UserRole).toString().toAscii().constData()) == -1)
	   return;

	if (gmn)
	   garmin_free_data (gmn);

	gmn = spw.readFile();
	zfactor = 0;

	// Make sure, the currently not visible tabs will be redrawn as soon
	// as they are visible.
	switch (curTab)
	{
	   case 0: tabDirt1 = tabDirt2 = tabDirt3 = true; break;
	   case 1: tabDirt0 = tabDirt2 = tabDirt3 = true; break;
	   case 2: tabDirt0 = tabDirt1 = tabDirt3 = true; break;
	   case 3: tabDirt0 = tabDirt1 = tabDirt2 = true; break;
	}

	// Display the just loaded sport session
	DIRTY = true;
	showLaps();
	showTrack();
	showCurves();

	if (curTab == 2)
	{
	   showThreeCurve();
	   tabDirt2 = false;
	}

	DIRTY = false;
}

/*
 * The following function is called, when the user clicks the print button.
 * It then prints the currently loaded activity. First the laps and on a
 * seperate page the map, heart rate, elevation and speed.
 *
 * The function uses the already existing function showTrack() to draw an
 * optional map and the track on it. It uses the function showThreeCurve()
 * to draw the heart rate, elevation and speed. Only the laps are drawn
 * directly in this function.
 */
void sportwatcherWidget::filePrint()
{
QPrinter printer(QPrinter::HighResolution);
QDateTime dt;
QTime t, st;
QDateTime *qt;
QRectF rect;
QString qs_name, qs_distance, qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed;
QString qs_calories, qs_avghr, qs_maxhr, qs_avgcadence, qs_ascent, qs_descent;
QString qs_totdist, hv0;
LAP *lap;
POINT *point;
RUN_NODE *rakt, *rn;
int laps, i, anz, cad, page, firstPage, lastPage;
qreal extMil[4];
double sum_asc, sum_dsc, old_asc, old_dsc;
double totdist, lineH, aktLine;
bool printed = false;

	if (!gmn)
	{
	   KMessageBox::error(this, i18n("You must select an activity first!"));
	   return;
	}

	printer.setCreator(QString("SportWatcher"));
	printer.setDocName(QString("SportWatcher_%1").arg(QString(VERSION)));
	QPrintDialog printDialog(&printer, this);

	if (!printDialog.exec() == QDialog::Accepted)
	   return;

	if (!(rn = ds.getRunNode()))
	   return;

	// We dont care about page margins of a printer. Instead we assume
	// a frame from about 20mm arround the page. European paper size is
	// A4 (210x297mm) and the output is optimized for this.
	totdist = 0;
	extMil[0] = 20.0;
	extMil[1] = 20.0;
	extMil[2] = 210.0 - 20.0;
	extMil[3] = 279.0 - 20.0;
	lineH = 4.5;	// The height of a line with 10pt letters
	page = 1;	// The page number
	firstPage = printer.fromPage();
	lastPage = printer.toPage();

	if (!printArea.begin(&printer))
	{
	   KMessageBox::error(this, i18n("Error initializing printer! Printing is currently not available!"));
	   return;
	}

	ActivePrint = true;
	rakt = rn;
	QApplication::setOverrideCursor (QCursor(Qt::WaitCursor));

	while (rakt)
	{
	   // Check for a supported run type
	   if (rakt->run->type == data_D1000 || rakt->run->type == data_D1009 ||
	   	rakt->run->type == data_D1010)
	   {
	   int ahr;
	   double distance, speed, mspeed;
	   QDate dat;

	      printArea.setFont(QFont(QString("Helvetica"), 12, QFont::Bold, false));
	      rect.setCoords(milToPixel(extMil[0], printer),
			milToPixel(extMil[1], printer, true),
			milToPixel(extMil[0]+170, printer),
			milToPixel(extMil[1] + 7.0, printer, true));
	      printArea.setPen(QPen(QBrush(QColor("black")), (qreal)milToPixel(0.5, printer, false)));

	      if ((lastPage > 0 && page >= firstPage && page <= lastPage) ||
		  (lastPage == 0 && firstPage == 0))
	      {
		 printArea.drawRect(rect);
		 printed = true;
	      }

	      if (!(lap = ds.getLap(rakt->run->first_lap_index)))
	      {
		 ActivePrint = false;
		 QApplication::restoreOverrideCursor();
		 return;
	      }

	      if (strlen (rn->run->workout.name) > 1 && strlen (rn->run->workout.name) < 16 && isalpha (rn->run->workout.name[0]))
		 hv0 = QString(rakt->run->workout.name);
	      else
	      {
		 qt = garmin_dtime(lap->start_time);
		 hv0 = kl->formatDate(qt->date(), KLocale::ShortDate) + " ";
		 hv0.append(qt->toString("hh:mm:ss"));
		 delete qt;
	      }

	      // Set the name depending on the sport type
	      // This is used on the tab "Summary"
	      switch (rakt->run->sport_type)
	      {
		 case D1000_running: qs_name = i18n("Running: "); break;
		 case D1000_biking:  qs_name = i18n("Biking: "); break;
		 case D1000_other:   qs_name = i18n("Other: "); break;
		 default:
		    qs_name = i18n("Unknown: ");
	      }

	      // Print the headline inside the frame
	      if ((lastPage > 0 && page >= firstPage && page <= lastPage) ||
		  (lastPage == 0 && firstPage == 0))
	      {
		 printArea.drawText(rect, Qt::AlignHCenter, QString("%1%2").arg(qs_name).arg(hv0));
		 // Print the headline of the table
		 printArea.setFont(QFont(QString("Helvetica"), 10, QFont::Bold, false));
		 rect.setCoords(milToPixel(extMil[0], printer), milToPixel(extMil[1]+10, printer, true), milToPixel(extMil[0]+35, printer), milToPixel(extMil[0]+10+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignHCenter, i18n("Lap"));
		 rect.setCoords(milToPixel(extMil[0]+35, printer), milToPixel(extMil[1]+10, printer, true), milToPixel(extMil[0]+60, printer), milToPixel(extMil[0]+10+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignHCenter, i18n("Distance"));
		 rect.setCoords(milToPixel(extMil[0]+60, printer), milToPixel(extMil[1]+10, printer, true), milToPixel(extMil[0]+85, printer), milToPixel(extMil[0]+10+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignHCenter, i18n("Time"));
		 rect.setCoords(milToPixel(extMil[0]+85, printer), milToPixel(extMil[1]+10, printer, true), milToPixel(extMil[0]+122, printer), milToPixel(extMil[0]+10+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignHCenter, i18n("Speed"));
		 rect.setCoords(milToPixel(extMil[0]+122, printer), milToPixel(extMil[1]+10, printer, true), milToPixel(extMil[0]+147, printer), milToPixel(extMil[0]+10+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignHCenter, i18n("Heart rate"));
		 rect.setCoords(milToPixel(extMil[0]+147, printer), milToPixel(extMil[1]+10, printer, true), milToPixel(extMil[0]+170, printer), milToPixel(extMil[0]+10+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignHCenter, i18n("Cadence"));
		 aktLine = extMil[1] + 10.0 + lineH;
		 printArea.drawLine(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0] + 170, printer), milToPixel(aktLine, printer, true));
		 aktLine += 1.0;
		 printed = true;
	      }
	      else
	      {
		 aktLine = extMil[1] + 10.0 + lineH;
		 aktLine += 1.0;
	      }

	      // Prepare to print the first line
	      qt = garmin_dtime (lap->start_time);
	      StartTime = *qt;
	      st = qt->time();
	      dat = qt->date();
	      delete qt;
	      qt = 0;
	      // Find the last track
	      if (!(point = ds.getLastPoint()))
	      {
		 QApplication::restoreOverrideCursor();
		 KMessageBox::error(this, i18n("Error getting the last messure point!"));
		 ActivePrint = false;
		 return;
	      }

	      qt = garmin_dtime(point->time);
	      t = qt->time();
	      t.setHMS(0, 0, 0);
	      t = t.addSecs(ds.getTotalTime());
	      qt->setDate(dat);
	      qt->setTime(t);
	      qs_name = kl->formatDate(dat, KLocale::ShortDate);
	      qs_name.append(" ");
	      qs_name.append(kl->formatTime(st, true));
	      max_time = ds.getTotalTime();
	      qs_etime = QString(qt->toString("hh:mm:ss.zzz"));

	      distance = 0.0;
	      mspeed = 0;
	      ahr = 0;
	      anz = 0;
	      cad = 0;
	      sum_asc = sum_dsc = old_asc = old_dsc = 0;

	      // Find out the total distance, calories, maximum speed,
	      // maximum heart rate and the average heart rate. Get this
	      // values from the lap summary the watch made.
	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
	      {
		 if ((lap = ds.getLap(i)) == NULL)
		    continue;

		 distance += lap->total_distance;

		 if (lap->avg_cadence != 0xff)
		    cad += lap->avg_cadence;

		 ahr += lap->avg_heart_rate;
		 anz++;

		 if (lap->max_speed > mspeed)
		    mspeed = lap->max_speed;
	      }

	      total_distance = distance = ds.getTotalDistance();

	      if (Units == 1)		// Statute?
		 qs_distance.sprintf("%.2f ft", distance / 0.304);
	      else
	         qs_distance.sprintf("%.2f m", distance);

	      if (distance > 0)
	      {
		 QTime tt = qt->time();
		 long secs = (double)(tt.hour() * 3600 + tt.minute() * 60 + tt.second()) / 100.0;

		 if (Units == 0)
		    secs = secs * (1000.0 / distance * 100.0);
		 else
		    secs = secs * (1609.344 / distance * 100.0);

		 int h = secs / 3600;
		 int m = (secs - (h * 3600)) / 60;
		 int s = secs - ((h * 3600) + (m * 60));
		 t = QTime(h, m, s, 0);
		 qs_avgpace = QString("  ") + kl->formatTime(t, true);

		 if (Units == 0)
		    qs_avgpace.append(QString(" /km"));
		 else
		    qs_avgpace.append(QString(" /mi"));
	      }

	      if (Units == 1)		// Statute?
		 speed = distance / ds.getTotalTime() * 3.6 / 1.609344;
	      else
		 speed = distance / ds.getTotalTime() * 3.6;

	      qs_avgspeed.sprintf("%.2f %s", speed, (Units == 1) ? "mph" : "km/h");
	      qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? mspeed * 3.6 / 1.609344 : mspeed * 3.6, (Units == 1) ? "mph" : "km/h");
	      qs_avghr.sprintf("%d bpm", ahr / anz);

	      if (cad > 0)
		 qs_avgcadence.sprintf("%d", cad / anz);

	      // Print out the summary line and draw twi thin lines underneath
	      if ((lastPage > 0 && page >= firstPage && page <= lastPage) ||
		  (lastPage == 0 && firstPage == 0))
	      {
		 printArea.setFont(QFont(QString("Helvetica"), 10, QFont::Normal, false));
		 rect.setCoords(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+35, printer), milToPixel(aktLine+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignLeft, qs_name);
		 rect.setCoords(milToPixel(extMil[0]+35, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+60, printer), milToPixel(aktLine+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignRight, kl->formatNumber(qs_distance, false));
		 rect.setCoords(milToPixel(extMil[0]+60, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+85, printer), milToPixel(aktLine+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignRight, qs_etime);
		 rect.setCoords(milToPixel(extMil[0]+85, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+122, printer), milToPixel(aktLine+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignRight, kl->formatNumber(qs_avgspeed, false));
		 rect.setCoords(milToPixel(extMil[0]+122, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+147, printer), milToPixel(aktLine+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignRight, qs_avghr);
		 rect.setCoords(milToPixel(extMil[0]+147, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+170, printer), milToPixel(aktLine+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignRight, qs_avgcadence);
		 aktLine += (lineH + (lineH / 3.0));
		 printArea.setPen(QPen(QBrush(QColor("black")), milToPixel(0.1, printer, false)));
		 printArea.drawLine(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0] + 170, printer), milToPixel(aktLine, printer, true));
		 printArea.drawLine(milToPixel(extMil[0], printer), milToPixel(aktLine+0.5, printer, true), milToPixel(extMil[0] + 170, printer), milToPixel(aktLine+0.5, printer, true));
		 aktLine += (lineH / 3.0);
		 printed = true;
	      }
	      else
	      {
		 aktLine += (lineH + (lineH / 3.0));
		 aktLine += (lineH / 3.0);
	      }

	      delete qt;
	      /* Get the laps. */
	      laps = 1;

	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
	      {
		 double spd;
		 char *un;

		 if ((lap = ds.getLap(i)) == NULL)
		    continue;

		 qt = garmin_dtime (lap->start_time);
		 qs_name.sprintf("Lap %03d - ", laps);
		 qs_name.append(kl->formatTime(qt->time(), true));
		 qs_distance.sprintf("%.2f %s", (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance, (Units == 1) ? "ft" : "m");
		 totdist += (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance;
		 qs_totdist.sprintf("%2.f %s", totdist, (Units == 1) ? "ft" : "m");
		 t = QTime(0, 0, 0, 0);
		 t = t.addMSecs(lap->total_time * 10);
		 qs_etime = t.toString("hh:mm:ss.zzz");
		 spd = lap->total_distance / (lap->total_time / 100.0);
		 delete qt;
		 qt = 0;

		 if (Units == 0)
		 {
		    un = (char *)"km/h";
		    spd *= 3.6;
		 }
		 else
		 {
		    spd *= 3.6 / 1.609344;
		    un = (char *)"mph";
		 }

		 qs_avgspeed.sprintf("%.2f %s", spd, un);

		 if (lap->total_distance > 0 && lap->total_time != 0)
		 {
		    double fact;

		    if (Units == 0)
		       fact = 1000.0;		// 1 km
		    else
		       fact = 1609.344;		// 1 mile in meters

		    long secs = (double)lap->total_time / 10000.0 * (fact / lap->total_distance * 100.0);
		    int h = secs / 3600;
		    int m = (secs - (h * 3600)) / 60;
		    int s = secs - ((h * 3600) + (m * 60));
		    t = QTime(h, m, s, 0);
		    qs_avgpace = kl->formatTime(t, true);

		    if (Units == 0)
		       qs_avgpace.append(QString(" /km"));
		    else
		       qs_avgpace.append(QString(" /mi"));
		 }

		 qs_avghr.sprintf("%d bpm", lap->avg_heart_rate);

		 if (lap->avg_cadence != 0xff)
		    qs_avgcadence.sprintf("%d", lap->avg_cadence);
		 // Draw a new detail line
		 if ((lastPage > 0 && page >= firstPage && page <= lastPage) ||
		     (lastPage == 0 && firstPage == 0))
		 {
		    rect.setCoords(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+35, printer), milToPixel(aktLine+lineH, printer, true));
		    printArea.drawText(rect, Qt::AlignLeft, qs_name);
		    rect.setCoords(milToPixel(extMil[0]+35, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+60, printer), milToPixel(aktLine+lineH, printer, true));
		    printArea.drawText(rect, Qt::AlignRight, kl->formatNumber(qs_distance, false));
		    rect.setCoords(milToPixel(extMil[0]+60, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+85, printer), milToPixel(aktLine+lineH, printer, true));
		    printArea.drawText(rect, Qt::AlignRight, qs_etime);
		    rect.setCoords(milToPixel(extMil[0]+85, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+122, printer), milToPixel(aktLine+lineH, printer, true));
		    printArea.drawText(rect, Qt::AlignRight, kl->formatNumber(qs_avgspeed, false));
		    rect.setCoords(milToPixel(extMil[0]+122, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+147, printer), milToPixel(aktLine+lineH, printer, true));
		    printArea.drawText(rect, Qt::AlignRight, qs_avghr);
		    rect.setCoords(milToPixel(extMil[0]+147, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+170, printer), milToPixel(aktLine+lineH, printer, true));
		    printArea.drawText(rect, Qt::AlignRight, qs_avgcadence);
		    printed = true;
		 }

		 aktLine += lineH;

		 if (aktLine >= extMil[3])	// Print on the next page
		 {
		    aktLine = extMil[3] + 10.0;

		    if ((lastPage > 0 && page >= firstPage && page <= lastPage) ||
		        (lastPage == 0 && firstPage == 0))
		    {
		       printArea.setFont(QFont(QString("Helvetica"), 8, QFont::Normal, false));
		       rect.setCoords(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+170, printer), milToPixel(aktLine+lineH, printer, true));
		       printArea.drawText(rect, Qt::AlignRight, i18n("Page %1").arg(page));
		       aktLine = extMil[1];
		       printed = true;
		    }
		    else
		       aktLine = extMil[1];

		    if (printed)
		       printer.newPage();

		    page++;
		    // Print the headline of the table
		    if ((lastPage > 0 && page >= firstPage && page <= lastPage) ||
		        (lastPage == 0 && firstPage == 0))
		    {
		       printArea.setFont(QFont(QString("Helvetica"), 10, QFont::Bold, false));
		       rect.setCoords(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+35, printer), milToPixel(aktLine+lineH, printer, true));
		       printArea.drawText(rect, Qt::AlignHCenter, i18n("Lap"));
		       rect.setCoords(milToPixel(extMil[0]+35, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+60, printer), milToPixel(aktLine+lineH, printer, true));
		       printArea.drawText(rect, Qt::AlignHCenter, i18n("Distance"));
		       rect.setCoords(milToPixel(extMil[0]+60, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+85, printer), milToPixel(aktLine+lineH, printer, true));
		       printArea.drawText(rect, Qt::AlignHCenter, i18n("Time"));
		       rect.setCoords(milToPixel(extMil[0]+85, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+122, printer), milToPixel(aktLine+lineH, printer, true));
		       printArea.drawText(rect, Qt::AlignHCenter, i18n("Speed"));
		       rect.setCoords(milToPixel(extMil[0]+122, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+147, printer), milToPixel(aktLine+lineH, printer, true));
		       printArea.drawText(rect, Qt::AlignHCenter, i18n("Heart rate"));
		       rect.setCoords(milToPixel(extMil[0]+147, printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0]+170, printer), milToPixel(aktLine+lineH, printer, true));
		       printArea.drawText(rect, Qt::AlignHCenter, i18n("Cadence"));
		       aktLine += lineH;
		       printArea.setPen(QPen(QBrush(QColor("black")), milToPixel(0.5, printer, false)));
		       printArea.drawLine(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true), milToPixel(extMil[0] + 170, printer), milToPixel(aktLine, printer, true));
		       aktLine += 1.0;
		       printed = true;
		    }
		    else
		    {
		       aktLine += lineH;
		       aktLine += 1.0;
		    }

		    printArea.setFont(QFont(QString("Helvetica"), 10, QFont::Normal, false));
		 }

		 delete qt;
		 laps++;
	      }

	      aktLine = extMil[3] + 10.0;
	      if ((lastPage > 0 && page >= firstPage && page <= lastPage) ||
		  (lastPage == 0 && firstPage == 0))
	      {
		 printArea.setFont(QFont(QString("Helvetica"), 8, QFont::Normal, false));
		 rect.setCoords(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				milToPixel(extMil[0]+170, printer),
				milToPixel(aktLine+lineH, printer, true));
		 printArea.drawText(rect, Qt::AlignRight, i18n("Page %1").arg(page));
		 printed = true;
	      }

	      aktLine = extMil[1];
	      page++;

	      if (printed && (page <= lastPage || lastPage == 0))
		 printer.newPage();

	      if ((lastPage > 0 && (page < firstPage || page > lastPage)))
		 break;

	      // Draw the map on top of a new page
	      // Use 1/3 of the available height for the map
	      qreal resFact = 8;	// The factor to calculate the resolution for pixmaps
	      int width = (int)(milToPixel(170, printer) / resFact);
	      int height = (int)(milToPixel(extMil[3] - extMil[1], printer, true) / resFact);
	      pmPrMap = QPixmap(width, height / 3);	// 1/3 of page height
	      DIRTY = true;
	      int ct = curTab;
	      curTab = 1;
	      QApplication::restoreOverrideCursor();
	      showTrack();
	      QApplication::setOverrideCursor (QCursor(Qt::WaitCursor));
	      DIRTY = false;
	      curTab = ct;
	      printArea.drawPixmap(milToPixel(extMil[0], printer),
				milToPixel(extMil[1], printer),
				pmPrMap.scaled((int)milToPixel(170, printer),
				(int)milToPixel(extMil[3] - extMil[1], printer, true) / 3,
				Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
	      // Put a frame around the map
	      rect.setCoords(milToPixel(extMil[0], printer), milToPixel(extMil[1], printer, true),
				milToPixel(extMil[0] + 170, printer),
				milToPixel(extMil[1], printer, true) + (milToPixel(extMil[3] - extMil[1], printer, true) / 3));
	      printArea.setPen(QPen(QBrush(QColor("black")), milToPixel(0.2, printer)));
	      printArea.drawRect(rect);
	      aktLine = (extMil[3] - extMil[1]) / 3.0 + extMil[1] + lineH;

	      // Draw the heart rate diagram
	      rect.setCoords(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				milToPixel(extMil[2], printer),
				milToPixel(aktLine + lineH, printer, true));
	      printArea.drawText(rect, Qt::AlignLeft, i18n("Heart rate:"));
	      aktLine += lineH;
	      qreal pixHeight = (extMil[3] - aktLine) / 3.0 - lineH * 2.0;
	      int realH = (int)milToPixel(pixHeight, printer, true);
	      height = realH / (int)resFact;
	      showThreeCurve(width, height);	// Calculate the curves
	      printArea.drawPixmap(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				prHR.scaled((int)milToPixel(170, printer),
				realH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
	      rect.setCoords(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true),
				milToPixel(extMil[0] + 170, printer),
				milToPixel(aktLine + pixHeight, printer, true));
	      printArea.drawRect(rect);
	      aktLine += pixHeight + lineH;

	      // Draw the elevation diagram
	      rect.setCoords(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				milToPixel(extMil[2], printer),
				milToPixel(aktLine + lineH, printer, true));
	      printArea.drawText(rect, Qt::AlignLeft, i18n("Elevation:"));
	      aktLine += lineH;
	      printArea.drawPixmap(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				prElevation.scaled((int)milToPixel(170, printer),
				realH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
	      rect.setCoords(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true),
				milToPixel(extMil[0] + 170, printer),
				milToPixel(aktLine + pixHeight, printer, true));
	      printArea.drawRect(rect);
	      aktLine += pixHeight + lineH;

	      // Draw the speed diagram
	      rect.setCoords(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				milToPixel(extMil[2], printer),
				milToPixel(aktLine + lineH, printer, true));
	      printArea.drawText(rect, Qt::AlignLeft, i18n("Speed:"));
	      aktLine += lineH;
	      printArea.drawPixmap(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				prSpeed.scaled((int)milToPixel(170, printer),
				realH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
	      rect.setCoords(milToPixel(extMil[0], printer), milToPixel(aktLine, printer, true),
				milToPixel(extMil[0] + 170, printer),
				milToPixel(aktLine + pixHeight, printer, true));
	      printArea.drawRect(rect);

	      // Print the page number at the right bottom of the page
	      aktLine = extMil[3] + 10.0;
	      printArea.setFont(QFont(QString("Helvetica"), 8, QFont::Normal, false));
	      rect.setCoords(milToPixel(extMil[0], printer),
				milToPixel(aktLine, printer, true),
				milToPixel(extMil[0]+170, printer),
				milToPixel(aktLine+lineH, printer, true));
	      printArea.drawText(rect, Qt::AlignRight, i18n("Page %1").arg(page));
	   }

	   rakt = rakt->next;
	}

	printArea.end();
	// Mark printing as done
	ActivePrint = false;
	QApplication::restoreOverrideCursor();
}

qreal sportwatcherWidget::milToPixel(qreal dist, QPrinter &pr, bool dir)
{
	QSizeF r = pr.paperSize(QPrinter::DevicePixel);
	QSizeF m = pr.paperSize(QPrinter::Millimeter);

	if (!dir)	// width
	   return r.width() / m.width() * dist;
	else
	   return r.height() / m.height() * dist;
}

/*
 * This function allows the user to choose a file name, where we save the
 * actual lap in Garmins own TCX format. This format is simply a XML-file.
 * For details about the schema of this file look at
 * http://developer.garmin.com/schemas/tcx/v2/
 */
void sportwatcherWidget::fileSaveAs()
{
QString fname;
QFile fn;
QString buffer;
RUN_NODE *rn, *rakt;
LAP *lap;
POINT *point;
int indent, i;
QDateTime *qt;
KUrl sDir("~/");
QRegExp rx("(\\.tcx|\\.gpx|\\.osm)$");

	if (!gmn)
	{
	   KMessageBox::error(this, i18n("Currently no activity is selected!"));
	   return;
	}

	rx.setPatternSyntax(QRegExp::RegExp);
	fname = KFileDialog::getSaveFileName(sDir, QString("*.tcx|Garmin Training Center (*.tcx)\n*.gpx|GPS Excange Format (*.gpx)\n*.osm|OpenStreetMap (*.osm)"), this, QString("SportWatcher"));

	if (fname.isEmpty())
	   return;

	if (rx.indexIn(fname) < 0)
	{
	   KMessageBox::error(this, i18n("The file %1 has no valid file extension!").arg(fname));
	   return;
	}

	fn.setFileName(fname);

	if (fn.exists())
	{
	   if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
	      return;
	}

	rx.setPattern("*.gpx");
	rx.setPatternSyntax(QRegExp::Wildcard);

	if (rx.exactMatch(fname))	// Should we create a *.gpx file?
	{
	   sportwatcherWidget::saveGPX(fname);
	   return;
	}

	rx.setPattern("*.osm");
	rx.setPatternSyntax(QRegExp::Wildcard);

	if (rx.exactMatch(fname))	// Should we create a *.osm file?
	{
	   sportwatcherWidget::saveOSM(fname);
	   return;
	}

	// No, we create a *.tcx file!
	indent = 0;
	rn = ds.getRunNode();
	lap = ds.getLap(rn->run->first_lap_index);

	if ((point = ds.getPoint(lap->start_time)) == 0)
	{
	   KMessageBox::error(this, i18n("No data to save!"));
	   return;
	}

	if (!fn.open(QIODevice::ReadWrite | QIODevice::Truncate))
	{
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fname));
	   return;
	}

	buffer = QString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
	buffer.append("<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
	buffer.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
	buffer.append("xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 ");
	buffer.append("http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n\n");
	writeTag (fn, buffer, indent);
	buffer = QString("<folders/>\n\n");
	writeTag (fn, buffer, indent);

	// Open a course
	QFileInfo finfo(fname);
	buffer = QString("<Courses>\n   <Course>\n      <name>%1</name>\n").arg(QFileInfo(finfo).baseName());
	writeTag (fn, buffer, indent);
	indent = 2;

	rakt = rn;

	while (rakt)
	{
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
	   	rakt->run->type != data_D1010)
	   {
	      rakt = rakt->next;
	      continue;
	   }

	   for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
	   {
	      if ((lap = ds.getLap(i)) == NULL)
		 continue;

	      // Write the information of the lap
	      writeTag (fn, QString("<Lap>\n"), indent);
	      indent++;
	      buffer.sprintf("<TotalTimeSeconds>%f</TotalTimeSeconds>\n", (double)lap->total_time / 100.0);
	      writeTag (fn, buffer, indent);
	      qt = garmin_dtime(lap->start_time);
	      buffer = QString("<StartTime>%1</StartTime>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
	      writeTag (fn, buffer, indent);
	      buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", lap->total_distance);
	      writeTag (fn, buffer, indent);

	      writeTag (fn, QString("<BeginPosition>\n"), indent);
	      indent++;
	      buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->begin.lat));
	      writeTag (fn, buffer, indent);
	      buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->begin.lon));
	      writeTag (fn, buffer, indent);
	      indent--;
	      writeTag (fn, QString("</BeginPosition>\n"), indent);

	      writeTag (fn, QString("<EndPosition>\n"), indent);
	      indent++;
	      buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->end.lat));
	      writeTag (fn, buffer, indent);
	      buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->end.lon));
	      writeTag (fn, buffer, indent);
	      indent--;
	      writeTag (fn, QString("</EndPosition>\n"), indent);

	      writeTag (fn, QString("<AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
	      indent++;
	      buffer.sprintf("<Value>%d</Value>\n", lap->avg_heart_rate);
	      writeTag (fn, buffer, indent);
	      indent--;
	      writeTag (fn, QString("</AverageHeartRateBpm>\n"), indent);

	      writeTag (fn, QString("<MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
	      indent++;
	      buffer.sprintf("<Value>%d</Value>\n", lap->max_heart_rate);
	      writeTag (fn, buffer, indent);
	      indent--;
	      writeTag (fn, QString("</MaximumHeartRateBpm>\n"), indent);

	      if (lap->avg_cadence < 255)
	      {
		 buffer.sprintf("<AverageCadence>%d</AverageCadence>\n", lap->avg_cadence);
		 writeTag (fn, buffer, indent);
		 buffer.sprintf("<Cadence>%d</Cadence>\n", lap->avg_cadence);
		 writeTag (fn, buffer, indent);
	      }

	      buffer = QString("<Intensity>%1</Intensity>\n").arg((!lap->intensity) ? "Active" : "Resting");
	      writeTag (fn, buffer, indent);

	      buffer.sprintf("<Calories>%d</Calories>\n", lap->calories);
	      writeTag (fn, buffer, indent);

	      buffer.sprintf("<MaximumSpeed>%f</MaximumSpeed>\n", lap->max_speed);
	      writeTag (fn, buffer, indent);
	      indent--;
	      writeTag (fn, QString("</Lap>\n"), indent);

	      point = ds.getPoint(lap->start_time);
	      writeTag (fn, QString("<Track>\n"), indent);
	      indent++;

	      while (point)
	      {
		 if (point->time > (lap->start_time + (lap->total_time / 100)))
		    break;

		 writeTag (fn, QString("<Trackpoint>\n"), indent);
		 indent++;
		 qt = garmin_dtime(point->time);
		 buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
		 writeTag (fn, buffer, indent);
		 delete qt;
		 writeTag (fn, QString("<Position>\n"), indent);
		 indent++;
		 buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(point->posn.lat));
		 writeTag (fn, buffer, indent);
		 buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(point->posn.lon));
		 writeTag (fn, buffer, indent);
		 indent--;
		 writeTag (fn, QString("</Position>\n"), indent);

		 if (point->alt < 20000.0)
		 {
		    buffer.sprintf("<AltitudeMeters>%f</AltitudeMeters>\n", point->alt);
		    writeTag (fn, buffer, indent);
		 }

		 buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", point->distance);
		 writeTag (fn, buffer, indent);

		 if (point->heart_rate > 0 && point->heart_rate < 250)
		 {
		    writeTag (fn, QString("<HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
		    indent++;
		    buffer.sprintf("<Value>%d</Value>\n", point->heart_rate);
		    writeTag (fn, buffer, indent);
		    indent--;
		    writeTag (fn, QString("</HeartRateBpm>\n"), indent);
		 }

		 if (point->cadence < 0xff)
		 {
		    buffer.sprintf("<Cadence>%d</Cadence>\n", point->cadence);
		    writeTag (fn, buffer, indent);
		 }

		 buffer.sprintf("<SensorState>%s</SensorState>\n", (!point->sensor) ? "Absent" : "Present");
		 writeTag (fn, buffer, indent);
		 indent--;
		 writeTag (fn, QString("</Trackpoint>\n"), indent);
		 point = ds.getPoint(point->time + 1);
	      }

	      indent--;
	      writeTag (fn, QString("</Track>\n"), indent);
	   }

	   indent--;
	   writeTag (fn, QString("</Course>\n"), indent);
	   indent--;
	   writeTag (fn, QString("</Courses>\n"), indent);
	   rakt = rakt->next;
	}

	// Write information about device
	// Here my personal signature is written :-)
	writeTag (fn, QString("<Author xsi:type=\"Application_t\">\n"), indent);
	indent++;
	writeTag (fn, QString("<Name>SportWatcher</Name>\n"), indent);
	writeTag (fn, QString("<Build>\n"), indent);
	indent++;
	writeTag (fn, QString("<Version>\n"), indent);
	indent++;
	writeTag (fn, QString("<VersionMajor>0</VersionMajor>\n"), indent);
	writeTag (fn, QString("<VersionMinor>1</VersionMinor>\n"), indent);
	writeTag (fn, QString("<BuildMajor>0</BuildMajor>\n"), indent);
	writeTag (fn, QString("<BuildMinor>0</BuildMinor>\n"), indent);
	indent--;
	writeTag (fn, QString("</Version>\n"), indent);
	writeTag (fn, QString("<Type>Beta</Type>\n"), indent);
	writeTag (fn, QString("<Time>Jan 31 2008, 00:00:00</Time>\n"), indent);
	writeTag (fn, QString("<Builder>theosys</Builder>\n"), indent);
	indent--;
	writeTag (fn, QString("</Build>\n"), indent);
	writeTag (fn, QString("<LangID>EN</LangID>\n"), indent);
	writeTag (fn, QString("<PartNumber>000-00000-00</PartNumber>\n"), indent);
	indent--;
	writeTag (fn, QString("</Author>\n"), indent);
	writeTag (fn, QString("</TrainingCenterDatabase>\n"), indent);

	fn.close();
	KMessageBox::information(this, i18n("File ") + fname + i18n(" was written successfully."));
}

void sportwatcherWidget::fileSave()
{
	KMessageBox::information(this, i18n("This function is currently not implemented!"));
}

void sportwatcherWidget::saveGPX(const QString &fn)
{
QFile qf;
QString buffer;
RUN_NODE *rn, *rakt;
LAP *lap;
POINT *point;
int indent;
unsigned int i;
QDateTime *qt;
double minLat, minLon, maxLat, maxLon;

	indent = 0;
	rn = ds.getRunNode();
	lap = ds.getLap(rn->run->first_lap_index);

	if ((point = ds.getPoint(lap->start_time)) == 0)
	{
	   KMessageBox::error(this, i18n("No data to save!"));
	   return;
	}

	qf.setFileName(fn);

	if (!qf.open(QIODevice::ReadWrite | QIODevice::Truncate))
	{
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fn));
	   return;
	}

	buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
	buffer.append("<gpx version=\"1.1\" creator=\"TheoSys SportWatcher\" xmlns=\"http://www.topografix.com/GPX/1/1\">\n");
	buffer.append("   <metadata>\n");
	indent = 0;
	writeTag (qf, buffer, indent);

	// Find the edges of our coordinates
	// We need this information in the header (metadata)
	rakt = rn;
	minLat = -90.0;
	minLon = -180.0;
	maxLat = 90.0;
	maxLon = 180.0;

	while (rakt)
	{
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
	   	rakt->run->type != data_D1010)
	   {
	      rakt = rakt->next;
	      continue;
	   }

	   i = rakt->run->first_lap_index;
	   // get the first lap
	   if ((lap = ds.getLap(i)) == NULL)
	      continue;

	   i = 0;
	   // iterate the points associated with the laps
	   while ((point = ds.getPoint(i)) != 0)
	   {
	      if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
	      {
		 i = point->time + 1;
		 continue;
	      }

	      if (SEMI2DEG(point->posn.lat) > minLat)
		 minLat = SEMI2DEG(point->posn.lat);

	      if (SEMI2DEG(point->posn.lat) < maxLat)
		 maxLat = SEMI2DEG(point->posn.lat);

	      if (SEMI2DEG(point->posn.lon) > minLon)
		 minLon = SEMI2DEG(point->posn.lon);

	      if (SEMI2DEG(point->posn.lon) < maxLon)
		 maxLon = SEMI2DEG(point->posn.lon);

	      i = point->time + 1;
	   }

	   rakt = rakt->next;
	}
	
	buffer.sprintf("      <bounds minlat=\"%f\" minlon=\"%f\" maxlat=\"%f\" maxlon=\"%f\" />\n",
		maxLat, minLon, minLat, maxLon);
	buffer.append("   </metadata>\n");
	buffer.append("   <trk>\n");
	buffer.append("      <trkseg>\n");
	writeTag (qf, buffer, indent);
	indent = 3;
	rn = ds.getRunNode();
	lap = ds.getLap(rn->run->first_lap_index);
	i = 0;

	while ((point = ds.getPoint(i)) != 0)
	{
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
	   {
	      i = point->time + 1;
	      continue;
	   }

	   buffer.sprintf("<trkpt lat=\"%f\" lon=\"%f\">\n",
		SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
	   writeTag(qf, buffer, indent);
	   indent++;
	   buffer.sprintf("<ele>%f</ele>\n", point->alt);
	   writeTag(qf, buffer, indent);
	   qt = garmin_dtime(point->time);
	   buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
	   writeTag(qf, buffer, indent);
	   indent--;
	   writeTag(qf, QString("</trkpt>\n"), indent);
	   i = point->time + 1;
	}

	indent = 0;
	buffer = QString("      </trkseg>\n");
	buffer.append("   </trk>\n");
	buffer.append("</gpx>\n");
	writeTag(qf, buffer, indent);
	qf.close();
	KMessageBox::information(this, i18n("File ") + fn + i18n(" was written successfully."));
}

void sportwatcherWidget::saveOSM(const QString &fn)
{
QFile qf;
QString buffer;
RUN_NODE *rn, *rakt;
LAP *lap;
POINT *point;
int indent, id, j;
unsigned int i;
double minLat, minLon, maxLat, maxLon;
QDateTime *qt;

	indent = 0;
	rn = ds.getRunNode();
	lap = ds.getLap(rn->run->first_lap_index);

	if ((point = ds.getPoint(lap->start_time)) == 0)
	{
	   KMessageBox::error(this, i18n("No data to save!"));
	   return;
	}

	qf.setFileName(fn);

	if (!qf.open(QIODevice::ReadWrite | QIODevice::Truncate))
	{
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fn));
	   return;
	}

	buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
	buffer.append("<osm version=\"0.5\" generator=\"TheoSys SportWatcher\">\n");
	indent = 0;
	writeTag (qf, buffer, indent);
	// Find the edges of our coordinates
	// We need this information in the header (metadata)
	rakt = rn;
	minLat = -90.0;
	minLon = -180.0;
	maxLat = 90.0;
	maxLon = 180.0;

	while (rakt)
	{
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
	   	rakt->run->type != data_D1010)
	   {
	      rakt = rakt->next;
	      continue;
	   }

	   i = rakt->run->first_lap_index;
	   // get the first lap
	   if ((lap = ds.getLap(i)) == NULL)
	      continue;

	   i = 0;
	   // iterate the points associated with the laps
	   while ((point = ds.getPoint(i)) != 0)
	   {
	      if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
	      {
		 i = point->time + 1;
		 continue;
	      }

	      if (SEMI2DEG(point->posn.lat) > minLat)
		 minLat = SEMI2DEG(point->posn.lat);

	      if (SEMI2DEG(point->posn.lat) < maxLat)
		 maxLat = SEMI2DEG(point->posn.lat);

	      if (SEMI2DEG(point->posn.lon) > minLon)
		 minLon = SEMI2DEG(point->posn.lon);

	      if (SEMI2DEG(point->posn.lon) < maxLon)
		 maxLon = SEMI2DEG(point->posn.lon);

	      i = point->time + 1;
	   }

	   rakt = rakt->next;
	}
	
	buffer.sprintf("   <bound box='%f,%f,%f,%f' origin='http://www.openstreetmap.org/api/0.5' />\n",
		maxLat, minLon, minLat, maxLon);
	writeTag (qf, buffer, indent);
	indent = 1;
	rn = ds.getRunNode();
	lap = ds.getLap(rn->run->first_lap_index);
	i = 0;
	id = -1;

	while ((point = ds.getPoint(i)) != 0)
	{
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
	   {
	      i = point->time + 1;
	      continue;
	   }

	   buffer.sprintf("<node id='%d' action='modify' visible='true' lat=\"%f\" lon=\"%f\">\n",
		id, SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
	   writeTag(qf, buffer, indent);
	   indent++;
	   buffer = QString("<tag k='created_by' v='TheoSys Sportwatcher' />\n");
	   writeTag(qf, buffer, indent);
	   buffer = QString("<tag k='highway' v='tertiary' />\n");
	   writeTag(qf, buffer, indent);
	   indent--;
	   writeTag(qf, QString("</node>\n"), indent);
	   id--;
	   i = point->time + 1;
	}

	qt = garmin_dtime(lap->start_time);
	buffer.sprintf("<way id='%d' action='modify' visible='true' timestamp='%s'>\n",
		id, QString(qt->toString("yyyy-MM-ddThh:mm:ssZ")).toAscii().data());
	writeTag(qf, buffer, indent);
	indent++;

	for (j = -1; j > id; j--)
	{
	   buffer.sprintf("<nd ref='%d' />\n", j);
	   writeTag(qf, buffer, indent);
	}

	indent--;
	writeTag(qf, QString("</way>\n"), indent);
	indent = 0;
	writeTag(qf, QString("</osm>\n"), indent);
	qf.close();
	KMessageBox::information(this, i18n("File %1 was written successfully.").arg(fn));
}

void sportwatcherWidget::fileOpen()
{
QString fname = KFileDialog::getOpenFileName(Data, QString("*.gmn"), this, QString("SportWatcher"));
int m;

        if (fname.isEmpty())
           return;

	spw.destroy();

        if (spw.setFileName(fname.toAscii().data()) == -1)
	   return;

	if (gmn)
	   garmin_free_data (gmn);

	gmn = spw.readFile();
	zfactor = 0;

	if ((m = garmin_count_error()) > 0)
	{
	int i, key = -1;

	   for (i = 0; i < m; i++)
	      KMessageBox::error(this, QString(garmin_get_next_error(&key)));

	   garmin_clear_errors();
	   return;
	}

	DIRTY = true;
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
	showLaps();
	showTrack();
	showCurves();

	if (curTab == 2)
	{
	   showThreeCurve();
	   tabDirt2 = false;
	}

	tabDirt0 = tabDirt3 = false;
	DIRTY = false;
}

void sportwatcherWidget::fileImport()
{
QString fname = KFileDialog::getOpenFileName(QString("~/"), QString("*.tcx"), this, QString("SportWatcher"));
gmn_import import;
int m;
QString tgfile, fld, px;
QPixmap qpx;
QFileInfo datei;
QList<QTreeWidgetItem *>item;
QTreeWidgetItem *el, *it;
LAP *lap;
RUN_NODE *rn;

        if (fname.isEmpty())
           return;

        import.setFile(fname);

	if ((m = import.import()) != 0)
	{
	   KMessageBox::error(this, QString(import.getError(m)));
	   return;
	}

	if (gmn)
	{
	   garmin_free_data (gmn);
	   gmn = 0;
	}

	if (!(gmn = import.getGarminData ()))
	   return;

	DIRTY = true;
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
	showLaps();
	showTrack();
	showCurves();

	if (curTab == 2)
	{
	   showThreeCurve();
	   tabDirt2 = false;
	}

	tabDirt0 = tabDirt3 = false;
	DIRTY = false;

	// Find the filename;
	// It consists of the date and the time.
	// We need this information to set the correct path to store the file.
	tgfile = Data;		// The base path
	rn = ds.getRunNode();
	lap = ds.getLap(rn->run->first_lap_index);
	QDateTime *qt = garmin_dtime (lap->start_time);
	tgfile.append (qt->toString ("/yyyy")); // year is a folder
	tgfile.append (qt->toString ("/MM"));	// month is a folder
	tgfile.append (qt->toString ("/yyyyMMddThhmmss"));	// The file name
	tgfile.append (".gmn");		// Extension of file name
	datei.setFile (tgfile);
	// save the data to a real file, but only if it doesn't exist allready.
	garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 0);

	// in case the item is already in the list on the left side, we
	// only highlight the item.
	item = ui_sportwatcherWidgetBase.liActivities->findItems (tgfile, Qt::MatchExactly);

	if (item.size() > 0)
	{
	   ui_sportwatcherWidgetBase.liActivities->setItemSelected (item.at(0), true);
	   ui_sportwatcherWidgetBase.liActivities->setCurrentItem (item.at(0));
	   return;
	}

	// insert everything into the list on the left side
	switch (rn->run->sport_type)
	{
	   case D1000_running:
	      fld = i18n("Running");
	      px = QString("spw-running");
	   break;

	   case D1000_biking:
	      fld = i18n("Biking");
	      px = QString("bike");
	   break;

	   default:
	      fld = i18n("Others");
	      px = QString("other");
	}

	// Do we have allready so items in the list?
	item = ui_sportwatcherWidgetBase.liActivities->findItems (fld, Qt::MatchExactly);

	if (item.size() > 0)
	{
	   el = new QTreeWidgetItem(item.at(0));
	   el->setText(0, kl->formatDateTime(*qt, KLocale::ShortDate, true));
	   el->setData(0, Qt::UserRole, tgfile);
	   el->setIcon(0, KIcon(px));
	}
	else	// no, this is the first item. (shouldn't be!)
	{
	   it = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
	   it->setText(0, fld);
	   it->setIcon(0, KIcon(QString("history")));

	   el = new QTreeWidgetItem(item.at(0));
	   el->setText(0, kl->formatDateTime(*qt, KLocale::ShortDate, true));
	   el->setData(0, Qt::UserRole, tgfile);
	   el->setIcon(0, KIcon(px));
	}
}

/*
 * Display a small dialog to rename the currently loaded session.
 */
void sportwatcherWidget::editRename()
{
bool ok;
QString name, inhalt;
QList<QTreeWidgetItem *> item;
QTreeWidgetItem *lvItem;
QFileInfo datei;
RUN_NODE *rn;
LAP *lap;
garmin_list *list;
D1009 *n;

	if (!gmn)
	{
	   KMessageBox::error(this, i18n("There is no session selected!"));
	   return;
	}
	
	rn = ds.getRunNode();
	item = ui_sportwatcherWidgetBase.liActivities->selectedItems ();
	lvItem = item.first();

	if (!isdigit(rn->run->workout.name[0]))
	   inhalt = lvItem->text(0);
	else
	   inhalt.clear();

	name = KInputDialog::getText(i18n("Rename session"), i18n("Session name"),
		inhalt, &ok, this, (QValidator *)0, QString("Nxxxxxxxxxxxxxx"),
		i18n("Enter a new name for the currently selected activity."));

	if (!ok)
	   return;

	if (name.length() <= 1)
	{
	   lap = ds.getLap(rn->run->first_lap_index);
	   const QDateTime *qt = garmin_dtime (lap->start_time);
	   QString idx = kl->formatDateTime(*qt, KLocale::ShortDate, true);
	   lvItem->setText (0, idx);
	   datei.setFile (lvItem->data(0, Qt::UserRole).toString());
	   garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 1);
	   delete qt;
	   return;
	}

	strncpy (rn->run->workout.name, name.toAscii().data(), 16);

	if (gmn->type != data_Dlist)
	{
	   KMessageBox::error(this, i18n("editRename: Unexpected structure type %1 found!").arg(gmn->type));
	   return;
	}

	list = (garmin_list *)gmn->data;

	if (list->head->data->type != data_D1009)	// This should be the run node
	{
	   KMessageBox::error(this, i18n("editRename: The run node was not found!"));
	   return;
	}

	n = (D1009 *)list->head->data->data;
	strncpy (n->workout.name, rn->run->workout.name, 15);
	lvItem->setText (0, name);
	datei.setFile (lvItem->data(0, Qt::UserRole).toString());
	garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 1);
}

void sportwatcherWidget::fileNew()
{
progressWidget *dlg = new progressWidget(this);

	dlg->show();

	if (!dlg->Download())
	{
	int m, key;

	   key = -1;

	   for (m = 0; m < garmin_count_error(); m++)
	      KMessageBox::error(this, QString(garmin_get_next_error(&key)));
	}
	else
	   getActivities();

	garmin_clear_errors();
	delete dlg;
}

/*
 * This function is called, when the user clicks at the menu point
 * "Save Heart Rate".
 * First, a file dialog box is displayed, where the user can choose a
 * directory and a file name to save the heart rate.
 * If the file could successfully be created, the heart rate is saved
 * in the "HRM"-format. This is the native format Polar uses to store
 * heart rate data. I've choosen this format, because it's popular and
 * used by many other software too.
 */
void sportwatcherWidget::extrasSaveHR()
{
QString fname, str1, str2;
QFile fdfile;
QDateTime *qt, *oldqt;
QDate dat;
QTime t;
QDir dir = QDir::home();
char hv0[256];
RUN_NODE *rn;
LAP *lap, *alap;
POINT *point;
int samples, smp, seconds, anz, nsec, samsec;
int avgHeart, minHeart, maxHeart, aktHeart;
int secRange1, secRange2, secRange3, secAbove, secBeyond;

	if (!gmn)
	{
	   KMessageBox::information(this, i18n("There is no activity open"));
	   return;
	}

	if (HRM.isEmpty())
	   str1 = dir.path();
	else
	   str1 = HRM;

	str1 +=  "/" + StartTime.toString("yyyyMMddThhmmss.zzz.hrm");
	fname = KFileDialog::getSaveFileName(str1, QString("*.hrm"), this, QString("SportWatcher"));

	if (fname.isEmpty())
	   return;

	fdfile.setFileName(fname);

	if (fdfile.exists())
	{
	   if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
	      return;
	}

	if (!fdfile.open(QIODevice::ReadWrite | QIODevice::Truncate))
	{
	   KMessageBox::error(this, i18n("Error creating a file!\nPlease check permissions."));
	   return;
	}

	rn = ds.getRunNode();
	lap = ds.getLap(rn->run->first_lap_index);
	t = StartTime.time();
	dat = StartTime.date();

	if ((point = ds.getPoint(lap->start_time)) == 0)
	{
	   fdfile.close();
	   return;
	}

	strcpy (hv0, "[Params]\n");
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	str1 = dat.toString("yyyyMMdd");
	str2 = t.toString("hh:mm:ss.z");
	sprintf(hv0, "Version=106\nMonitor=11\nSMode=000000000\nDate=%s\nStartTime=%s\n",
		str1.toAscii().data(), str2.toAscii().data());
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	t.setHMS(0, 0, 0);
	t = t.addSecs(max_time);
	str2 = t.toString("hh:mm:ss.z");

	switch (sampleTime)
	{
	   case 0: samsec = 5; break;
	   case 1: samsec = 15; break;
	   case 2: samsec = 30; break;
	   case 3: samsec = 60; break;
	   default:
	      samsec = 15;
	}

	sprintf(hv0, "Length=%s\nInterval=%d\nUpper1=%d\nLower1=%d\nUpper2=%d\n",
	   str2.toAscii().data(), samsec, upper1, lower1, upper2);
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	sprintf(hv0, "Lower2=%d\nUpper3=%d\nLower3=%d\nTimer1=00:00:00.0\n",
		lower2, upper3, lower3);
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	strcpy(hv0, "Timer2=00:00:00.0\nTimer3=00:00:00.0\nActiveLimit=0\n");
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	sprintf(hv0, "MaxHR=%d\nRestHR=%d\nStartDelay=0\nVO2max=%d\nWeight=%d\n\n",
		MaxHr, restHr, vo2max, weight);
	write(fdfile.handle(), &hv0[0], strlen(hv0));

	// Write the intervall times. One block for every lap
	secRange1 = secRange2 = secRange3 = secAbove = secBeyond = 0;
	strcpy(hv0, "[IntTimes]\n");
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	t.setHMS(0, 0, 0);

	for (unsigned int i = rn->run->first_lap_index; i < rn->run->last_lap_index; i++)
	{
	   alap = ds.getLap(i);
	   point = ds.getPoint(alap->start_time);
	   oldqt = garmin_dtime(point->time);
	   avgHeart = minHeart = maxHeart = aktHeart = 0;
	   anz = 0;
	   unsigned long lastTime = point->time;
	   int totSec = 0;

	   while (point)
	   {
	      if (point->time > (alap->start_time + (alap->total_time / 100)))
		 break;

	      if (point->heart_rate > 0)
	      {
		 avgHeart += point->heart_rate;
		 nsec = point->time - lastTime;
		 totSec += nsec;

		 if (minHeart == 0 || minHeart > point->heart_rate)
		    minHeart = point->heart_rate;

		 if (maxHeart < point->heart_rate)
		    maxHeart = point->heart_rate;

		 if (aktHeart == 0 && totSec >= samsec)
		    aktHeart = avgHeart / (anz + 1);

		 if (point->heart_rate < lower1)
		    secBeyond += nsec;
		 else if (point->heart_rate < lower2)
		    secRange1 += nsec;
		 else if (point->heart_rate < lower3)
		    secRange2 += nsec;
		 else if (point->heart_rate < upper3)
		    secRange3 += nsec;
		 else
		    secAbove += nsec;

		 lastTime = point->time;
		 anz++;
	      }

	      point = ds.getPoint(point->time+1);
	   }

	   t = t.addSecs(alap->total_time / 100);
	   str1 = t.toString("hh:mm:ss.z");

	   if (anz > 0)
	      avgHeart = avgHeart / anz;
	   else
	      avgHeart = 0;

	   sprintf(hv0, "%s\t %d\t %d\t %d\t %d\n",
	      str1.toAscii().data(), aktHeart, minHeart, avgHeart, maxHeart);
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
	   strcpy(hv0, "32\t 0\t 0\t 0\t 0\t 0\n");
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
	   strcpy(hv0, "0\t 0\t 0\t 0\t 0\n");
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
	   sprintf(hv0, "0\t %d\t 0\t 0\t 0\t 0\n", (int)alap->total_distance);
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
	   strcpy(hv0, "0\t 0\t 0\t 0\t 0\t 0\n");
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
	}

	strcpy(hv0, "\n[IntNotes]\n\n[ExtraData]\n\n");
	write(fdfile.handle(), &hv0[0], strlen(hv0));

	strcpy(hv0, "[Summary-123]\n");
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 1
	smp = max_time - secBeyond - secRange1 - secRange2 - secRange3 - secAbove;
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
		max_time, secRange1, secRange2 + secRange3,
		secAbove + secBeyond, smp);
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 1
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
		MaxHr, upper1, lower1, restHr);
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 1
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
		max_time, secRange2, secRange1 + secRange3,
		secAbove + secBeyond, smp);
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 2
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
		MaxHr, upper2, lower2, restHr);
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 2
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
		max_time, secRange3, secRange1 + secRange2,
		secAbove + secBeyond, smp);
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 3
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
		MaxHr, upper3, lower3, restHr);
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 3
	samples = max_time / samsec;
	sprintf(hv0, "0\t %u\n\n", samples);	// samples
	write(fdfile.handle(), &hv0[0], strlen(hv0));

	strcpy(hv0, "[Summary-TH]\n");
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	sprintf(hv0, "%lu\t 0\t %lu\t %d\t %d\t 0\n", max_time, max_time - max_hr - restHr, max_hr, restHr);
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	sprintf(hv0, "%d\t %d\t %d\t %d\n", MaxHr, upper3, lower1, restHr);
	write(fdfile.handle(), &hv0[0], strlen(hv0));
	sprintf(hv0, "0\t %u\n\n", samples);	// samples
	write(fdfile.handle(), &hv0[0], strlen(hv0));

	sprintf(hv0, "[HRZones]\n%d\n%d\n%d\n%d\n%d\n%d\n0\n0\n0\n0\n0\n\n",
		MaxHr, upper3, upper2, upper1, lower1, restHr);
	write(fdfile.handle(), &hv0[0], strlen(hv0));

	strcpy(hv0, "[HRData]\n");
	write(fdfile.handle(), &hv0[0], strlen(hv0));

	smp = 0;		// average heart rate of 15 seconds
	seconds = 0;
	anz = 0;
	nsec = samsec;
	oldqt = garmin_dtime(lap->start_time);
	qt = 0;
	point = ds.getPoint(lap->start_time);

	while (point)
	{
	   if (seconds >= nsec)
	   {
	      if (anz > 0)
	      {
		 sprintf(hv0, "%d\n", smp / anz);
		 write(fdfile.handle(), &hv0[0], strlen(hv0));
	      }

	      if (smp > 0 && seconds >= (nsec + samsec))
	      {
		 if (anz <= 0)
		    anz = 0;

		 for (int x = nsec; x < seconds; x += samsec)
		 {
		    sprintf(hv0, "%d\n", smp / anz);
		    write(fdfile.handle(), &hv0[0], strlen(hv0));
		    nsec += samsec;
		 }
	      }
		
	      anz = 0;
	      smp = 0;
	      nsec += samsec;
	   }

	   qt = garmin_dtime (point->time);
	   seconds += oldqt->secsTo(*qt);

	   if (point->heart_rate > 0)
	   {
	      smp += point->heart_rate;
	      anz++;
	   }

	   delete oldqt;
	   oldqt = qt;
	   point = ds.getPoint(point->time + 1);
	}

	fdfile.close();
	KMessageBox::information(this, i18n("File successfully written."));
}

void sportwatcherWidget::extrasSettings()
{
settingsWidget *dlg = new settingsWidget(this);

	if (dlg->exec() == QDialog::Accepted)
	{
	   KConfig cfg(QString("sportwatcher.rc"), KConfig::SimpleConfig);
	   KConfigGroup ic (&cfg, "SportWatcher");
	   lower1 = ic.readEntry("lower1", 0);
	   lower2 = ic.readEntry("lower2", 0);
	   lower3 = ic.readEntry("lower3", 0);
	   upper1 = ic.readEntry("upper1", 0);
	   upper2 = ic.readEntry("upper2", 0);
	   upper3 = ic.readEntry("upper3", 0);
	   MaxHr = ic.readEntry("maxHr", 0);
	   restHr = ic.readEntry("restHr", 0);
	   vo2max = ic.readEntry("vo2max", 0);
	   weight = ic.readEntry("weight", 0);
	   sampleTime = ic.readEntry("seconds", 1);
	   Serial = ic.readEntry("Serial", false);
	   Contour = ic.readEntry("Contour", false);
	   Device = ic.readEntry("Device", QString("/dev/ttyUSB0"));
	   Forerunner = ic.readEntry("Forerunner", false);
	   Data = ic.readEntry("Data", QString("/"));
	   HRM = ic.readEntry("HRM", QString("/"));
	   MAP = ic.readEntry("MAP", QString("/"));
	   Units = ic.readEntry("Units", 0);
	   MapType = ic.readEntry("MapType", 0);
	}

	delete dlg;
}

void sportwatcherWidget::extrasWMSSettings()
{
#if defined HAVE_GDAL
	if (MapType == MPT_BMP || MapType == MPT_GIF || MapType == MPT_PNG ||
	    MapType == MPT_TIF)
	{
	   coordinatesWidget *idlg = new coordinatesWidget(this);
	   idlg->exec();
	   delete idlg;
	   return;
	}

	if (MapType == MPT_WMS)
	{
	   wmsbase *dlg = new wmsbase(this);
	   dlg->exec();
	   delete dlg;
	}
#if defined HAVE_MAPNIK
	if (MapType == MPT_SHP || MapType == MPT_OSM)
	{
	   shapeWidget *dlg = new shapeWidget(this);

	   if (MapType == MPT_SHP)
	      dlg->setMapType(shapeWidget::MAP_SHAPE);
	   else
	      dlg->setMapType(shapeWidget::MAP_OSM);

	   dlg->exec();
	   delete dlg;
	}
#else
	KMessageBox::detailedSorry(this,
	   i18n("This function was disabled at compile time because of missing Mapnik!"),
	   i18n("SportWatcher needs Mapnik 0.6 or newer, to enable this function.\n") +
	   i18n("If you like this to be working, install Mapnik and recompile the source."));
#endif
	if (MapType != MPT_WMS && MapType != MPT_SHP && MapType != MPT_OSM)
	{
	   KMessageBox::detailedSorry(this,
	      i18n("You have not choosen a WMS tag file or shape file directory!"),
	      i18n("This dialog is especialy to set map specific parameters. ") +
	      i18n("Therefore this dialog is temporary disabled. It will be ") +
	      i18n("available again, as soon as you choose \"WMS server\" or ") +
	      i18n("Shape file as your map type."));
	      return;
	}
#else
	KMessageBox::detailedSorry(this,
	   i18n("This function was disabled at compile time because of missing GDAL v1.x.x!"),
	   i18n("Sportwatcher needs GDAL v1.5.x or newer, to enable this function.\n") +
	   i18n("If you like this to be working, install GDAL and recompile the source!"));
#endif
}

/*
 * Functions to fill in the boxes of the main mask.
 */
void sportwatcherWidget::showLaps()
{
QString qs_name, qs_distance, qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed;
QString qs_calories, qs_avghr, qs_maxhr, qs_avgcadence, qs_ascent, qs_descent;
QString qs_totdist;
QDateTime dt;
QTime t, st;
QDateTime *qt;
LAP *lap;
POINT *point;
RUN_NODE *rakt, *rn;
int laps, i, anz, men, cad;
double alt_asc, alt_dsc, sum_asc, sum_dsc, old_asc, old_dsc;
double totdist;
bool pause;

	if (!DIRTY)
	   return;

	if (!gmn)
	   return;

	if (gmn->type == data_Dnil)
	{
	   KMessageBox::error(this, i18n("No data found!"));
	   return;
	}

	if (gmn->type != data_Dlist)     /* List of data */
	{
	   KMessageBox::error(this, i18n("Found unexpected data type %1!").arg(gmn->type));
	   return;
	}

	ds.destroy();
	min_hr = max_hr = avg_hr = 0;
	min_height = max_height = 0.0;
	min_speed = max_speed = 0.0;
	totdist = 0.0;
	// Tab Summary
	ui_sportwatcherWidgetBase.liLaps->clear();
	ui_sportwatcherWidgetBase.liLaps->setAllColumnsShowFocus(true);
	// Tab Laps
	ui_sportwatcherWidgetBase.twLaps->clear();
	ds.garmin_print_data(gmn);

	if (!(rn = ds.getRunNode()))
	   return;

	rakt = rn;
	// Tab Summary
	ui_sportwatcherWidgetBase.liLaps->setRootIsDecorated(true);

	for (i = 1; i < 12; i++)
	   ui_sportwatcherWidgetBase.liLaps->setColumnAlignment(i, Qt::AlignRight);
	// Tab Laps
	ui_sportwatcherWidgetBase.edTotalDistance->clear();
	ui_sportwatcherWidgetBase.edTotalTime->clear();
	ui_sportwatcherWidgetBase.edAvgSpeed->clear();
	ui_sportwatcherWidgetBase.edTotalHeight->clear();
	ui_sportwatcherWidgetBase.edAvgHR->clear();
	ui_sportwatcherWidgetBase.edLapNumber->clear();

	qs_name = qs_distance = qs_etime = qs_avgpace = qs_avgspeed = qs_maxspeed = QString("");
	qs_calories = qs_avghr = qs_maxhr = qs_avgcadence = qs_ascent = qs_descent = QString("");
	men = 0;
	cad = 0;

	// The main loop.
	// If a supported run type is detected, it will be processed.
	while (rakt)
	{
	   // Check for a supported run type
	   if (rakt->run->type == data_D1000 || rakt->run->type == data_D1009 ||
	   	rakt->run->type == data_D1010)
	   {
	   int cal, ahr, mhr;
	   double distance, speed, mspeed;
	   QDate dat;

	      // Set the name depending on the sport type
	      // This is used on the tab "Summary"
	      switch (rakt->run->sport_type)
	      {
		 case D1000_running: qs_name = i18n("Running: "); break;
		 case D1000_biking:  qs_name = i18n("Biking: "); break;
		 case D1000_other:   qs_name = i18n("Other: "); break;
		 default:
		    qs_name = i18n("Unknown: ");
	      }

	      if (!(lap = ds.getLap(rakt->run->first_lap_index)))
		 return;

	      qt = garmin_dtime (lap->start_time);
	      StartTime = *qt;
	      st = qt->time();
	      dat = qt->date();
	      delete qt;
	      qt = 0;
	      // Find the last track;
	      //    It is possible to delete laps directly on the watch,
	      //    so we can't be sure the last lap is really the last one.
	      //    Tracks are not deleted and the last track contains the
	      //    summuraries we need.
	      if (!(point = ds.getLastPoint()))
	      {
		 KMessageBox::error(this, i18n("Error getting the last messure point!"));
		 return;
	      }

	      qt = garmin_dtime(point->time);
	      t = qt->time();
	      t.setHMS(0, 0, 0);
	      t = t.addSecs(ds.getTotalTime());
	      qt->setDate(dat);
	      qt->setTime(t);
	      qs_name.append(kl->formatDate(dat, KLocale::ShortDate));
	      qs_name.append(" ");
	      qs_name.append(kl->formatTime(st, true));
	      max_time = ds.getTotalTime();
	      qs_etime = QString(qt->toString("hh:mm:ss.zzz"));

	      distance = 0.0;
	      cal = 0;
	      mspeed = 0;
	      ahr = mhr = 0;
	      anz = 0;
	      cad = 0;
	      sum_asc = sum_dsc = old_asc = old_dsc = 0;

	      // Find out the total distance, calories, maximum speed,
	      // maximum heart rate and the average heart rate. Get this
	      // values from the lap summary the watch made.
	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
	      {
		 if ((lap = ds.getLap(i)) == NULL)
		    continue;

		 distance += lap->total_distance;
		 cal += lap->calories;

		 if (lap->avg_cadence != 0xff)
		    cad += lap->avg_cadence;

		 ahr += lap->avg_heart_rate;
		 anz++;

		 if (lap->max_speed > mspeed)
		    mspeed = lap->max_speed;

		 if (lap->max_heart_rate > mhr)
		    mhr = lap->max_heart_rate;
	      }

	      total_distance = distance = ds.getTotalDistance();

	      if (Units == 1)		// Statute?
		 qs_distance.sprintf("%.2f ft", distance / 0.304);
	      else
	         qs_distance.sprintf("%.2f m", distance);

	      if (distance > 0)
	      {
		 QTime tt = qt->time();
		 long secs = (double)(tt.hour() * 3600 + tt.minute() * 60 + tt.second()) / 100.0;

		 if (Units == 0)
		    secs = secs * (1000.0 / distance * 100.0);
		 else
		    secs = secs * (1609.344 / distance * 100.0);

		 int h = secs / 3600;
		 int m = (secs - (h * 3600)) / 60;
		 int s = secs - ((h * 3600) + (m * 60));
		 t = QTime(h, m, s, 0);
		 qs_avgpace = QString("  ") + kl->formatTime(t, true);

		 if (Units == 0)
		    qs_avgpace.append(QString(" /km"));
		 else
		    qs_avgpace.append(QString(" /mi"));
	      }

	      if (Units == 1)		// Statute?
		 speed = distance / ds.getTotalTime() * 3.6 / 1.609344;
	      else
		 speed = distance / ds.getTotalTime() * 3.6;

	      qs_avgspeed.sprintf("%.2f %s", speed, (Units == 1) ? "mph" : "km/h");
	      qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? mspeed * 3.6 / 1.609344 : mspeed * 3.6, (Units == 1) ? "mph" : "km/h");
	      qs_calories.sprintf("%d", cal);
	      qs_avghr.sprintf("%d bpm", ahr / anz);
	      qs_maxhr.sprintf("%d bpm", mhr);

	      if (cad > 0)
		 qs_avgcadence.sprintf("%d", cad / anz);

	      // Add the summary line columns to the tab "Summary"
	      K3ListViewItem *element = new K3ListViewItem(ui_sportwatcherWidgetBase.liLaps,
	        qs_name, kl->formatNumber(qs_distance, false),
		qs_etime, qs_avgpace, kl->formatNumber(qs_avgspeed, false),
		kl->formatNumber(qs_maxspeed, false), qs_calories, qs_avghr);
	      element->setText(8, qs_maxhr);
	      element->setText(9, qs_avgcadence);
	      element->setText(10, kl->formatNumber(qs_ascent, false));
	      element->setText(11, kl->formatNumber(qs_descent, false));
	      element->sortChildItems(0, false);
	      element->setOpen(true);
	      element->setPixmap(0, KIcon(QString("activity")).pixmap(16));
	      ui_sportwatcherWidgetBase.liLaps->insertItem(element);
	      // Add some of the summaries to the fields on the tab "Lap details"
	      ui_sportwatcherWidgetBase.edTotalDistance->setAlignment(Qt::AlignRight);
	      ui_sportwatcherWidgetBase.edTotalDistance->insert(kl->formatNumber(qs_distance, false));
	      ui_sportwatcherWidgetBase.edTotalTime->setAlignment(Qt::AlignRight);
	      ui_sportwatcherWidgetBase.edTotalTime->insert(qs_etime);
	      ui_sportwatcherWidgetBase.edAvgSpeed->setAlignment(Qt::AlignRight);
	      ui_sportwatcherWidgetBase.edAvgSpeed->insert(kl->formatNumber(qs_avgspeed, false));
	      ui_sportwatcherWidgetBase.edTotalHeight->setAlignment(Qt::AlignRight);
	      ui_sportwatcherWidgetBase.edTotalHeight->insert(kl->formatNumber(qs_ascent, false));
	      ui_sportwatcherWidgetBase.edAvgHR->setAlignment(Qt::AlignRight);
	      ui_sportwatcherWidgetBase.edAvgHR->insert(qs_avghr);
	      delete qt;
	      /* Get the laps. */
	      laps = 1;

	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
	      {
		 double spd;
		 char *un;

		 if ((lap = ds.getLap(i)) == NULL)
		    continue;

		 qt = garmin_dtime (lap->start_time);
		 qs_name.sprintf("Lap %03d - ", laps);
		 qs_name.append(kl->formatTime(qt->time(), true));
		 qs_distance.sprintf("%.2f %s", (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance, (Units == 1) ? "ft" : "m");
		 totdist += (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance;
		 qs_totdist.sprintf("%2.f %s", totdist, (Units == 1) ? "ft" : "m");
		 t = QTime(0, 0, 0, 0);
		 t = t.addMSecs(lap->total_time * 10);
		 qs_etime = t.toString("hh:mm:ss.zzz");
		 spd = lap->total_distance / (lap->total_time / 100.0);
		 delete qt;
		 qt = 0;

		 if (Units == 0)
		 {
		    un = (char *)"km/h";
		    spd *= 3.6;
		 }
		 else
		 {
		    spd *= 3.6 / 1.609344;
		    un = (char *)"mph";
		 }

		 qs_avgspeed.sprintf("%.2f %s", spd, un);
		 qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? lap->max_speed * 3.6 / 1.609344 : lap->max_speed * 3.6, un);
		 qs_calories.sprintf("%d", lap->calories);

		 if (lap->total_distance > 0 && lap->total_time != 0)
		 {
		    double fact;

		    if (Units == 0)
		       fact = 1000.0;		// 1 km
		    else
		       fact = 1609.344;		// 1 mile in meters

		    long secs = (double)lap->total_time / 10000.0 * (fact / lap->total_distance * 100.0);
		    int h = secs / 3600;
		    int m = (secs - (h * 3600)) / 60;
		    int s = secs - ((h * 3600) + (m * 60));
		    t = QTime(h, m, s, 0);
		    qs_avgpace = kl->formatTime(t, true);

		    if (Units == 0)
		       qs_avgpace.append(QString(" /km"));
		    else
		       qs_avgpace.append(QString(" /mi"));
		 }

		 qs_avghr.sprintf("%d bpm", lap->avg_heart_rate);
		 qs_maxhr.sprintf("%d bpm", lap->max_heart_rate);

		 anz = 0;
		 alt_asc = alt_dsc = 0;
		 // Add a new detail line to the tab "Lap details"
		 QTreeWidgetItem * trdetail = new QTreeWidgetItem(ui_sportwatcherWidgetBase.twLaps);

		 if ((point = ds.getPoint(lap->start_time)) != 0)
		 {
		    if (point->alt < 20000)
		    {
		       alt_dsc = alt_asc = point->alt;

		       if (old_asc == 0)
			  old_asc = alt_asc;

		       if (old_dsc == 0)
			  old_dsc = alt_dsc;
		    }
		    else
		       alt_dsc = alt_asc = 0;

		    POINT *oldPoint = 0;
		    double sc, dist, speed;
		    unsigned long t1, t2;
		    t1 = t2 = 0;
		    pause = false;
		    bool ignore = false;

		    while (point)
		    {
		       if (point->time > (lap->start_time + (lap->total_time / 100)))
			 break;

		       QTreeWidgetItem *child = new QTreeWidgetItem(trdetail);
		       qt = garmin_dtime (point->time);
		       child->setText(0, kl->formatTime(qt->time(), true));
		       child->setTextAlignment(0, Qt::AlignRight);
		       child->setText(8, QString("%1 %2").arg(kl->formatNumber((double)point->heart_rate, 0)).arg(" bpm"));
		       child->setTextAlignment(8, Qt::AlignRight);

		       if (point->cadence < 0xff)
		       {
			  child->setText(10, kl->formatNumber((double)point->cadence, 0));
			  child->setTextAlignment(10, Qt::AlignRight);
		       }

		       if (point->alt < 20000)
		       {
		       double alt = (Units == 0) ? (double)point->alt : (double)point->alt / 0.304;

			  child->setText(11, QString("%1 %2").arg(kl->formatNumber(alt, 2)).arg((Units == 0) ? QString(" m") : QString(" ft")));
			  child->setTextAlignment(11, Qt::AlignRight);
		       }

		       if (!oldPoint)
			  oldPoint = point;

		       if (point->alt > alt_asc && point->alt < 20000)
		       {
			  alt_asc = point->alt;

			  if (alt_dsc == 0)
			     alt_dsc = point->alt;
		       }

		       if (point->alt < alt_dsc)
			  alt_dsc = point->alt;

		       // save the min and max values. We need this information to
		       // build the graphics.
		       if (point->heart_rate > max_hr && point->heart_rate < 250)
			  max_hr = point->heart_rate;

		       if ((min_hr == 0 && point->heart_rate > 0 && point->heart_rate < 250)
			   || (point->heart_rate > 0 && point->heart_rate < min_hr))
			  min_hr = point->heart_rate;

		       if (point->alt < 20000 && max_height < point->alt)
			  max_height = point->alt;

		       if (point->alt < 20000 && (min_height == 0.0 || min_height > point->alt))
			  min_height = point->alt;

		       // Calculate speed of current track
		       if (!pause && point->distance > 1.0e10)
		       {
			  t1 = point->time;
			  pause = true;
			  ignore = true;
		       }
		       else if (pause)
		       {
			  t2 = point->time;
			  pause = false;
			  point = ds.getPoint(point->time + 1);
			  continue;
		       }

		       if (!ignore && !pause)
		       {
			  sc = point->time - oldPoint->time;
			  dist = point->distance - oldPoint->distance;

			  child->setText(1, QString("%1 %2").arg(kl->formatNumber((Units == 0) ? (double)dist : (double)dist / 0.304, 2)).arg((Units == 0) ? QString(" m") : QString(" ft")));
			  child->setTextAlignment(1, Qt::AlignRight);

			  if (t2 > t1)
			     sc -= t2 - t1;

			  if (sc > 0.0)
			  {
			     speed = (dist / sc) * 3.6;

			     if (speed > lap->max_speed * 3.6)
			        speed = lap->max_speed * 3.6;
			  }
			  else
			     speed = 0.0;

			  if (Units == 1)
			     speed /= 1.609344;

			  child->setText(5, QString("%1 %2").arg(kl->formatNumber(speed, 2)).arg((Units == 0) ? QString(" Km/h") : QString(" mi/h")));
			  child->setTextAlignment(5, Qt::AlignRight);

			  if (speed > 0.0 && speed < 400.0 && max_speed < speed)
			     max_speed = speed;

			  if (speed > 0.0 && (min_speed == 0.0 || min_speed > speed))
			     min_speed = speed;

			  oldPoint = point;
		       }

		       if (!pause && ignore)
			  ignore = false;

		       if (point->heart_rate > 0 && point->heart_rate < 250)
		       {
			  avg_hr += point->heart_rate;
			  men++;
		       }

		       point = ds.getPoint(point->time + 1);
		    }

		    if (old_asc < alt_asc)
		       sum_asc += (alt_asc - old_asc);

		    if (old_dsc > alt_dsc)
		       sum_dsc += (old_dsc - alt_dsc);

		    old_asc = alt_asc;
		    old_dsc = alt_dsc;
		    qs_ascent.sprintf("%.2f %s", (Units == 1) ? (alt_asc / 0.304) : alt_asc, (Units == 1) ? "ft" : "m");
		    qs_descent.sprintf("%.2f %s", (Units == 1) ? (alt_dsc / 0.304) : alt_dsc, (Units == 1) ? "ft" : "m");
		 }

		 if (lap->avg_cadence != 0xff)
		    qs_avgcadence.sprintf("%d", lap->avg_cadence);
		 // Add a new detail line to the tab "Summary"
		 K3ListViewItem *edetail = new K3ListViewItem(element, qs_name,
			kl->formatNumber(qs_distance, false),
			qs_etime, qs_avgpace, kl->formatNumber(qs_avgspeed, false),
			kl->formatNumber(qs_maxspeed, false), qs_calories, qs_avghr);
		 edetail->setText(8, qs_maxhr);
		 edetail->setText(9, qs_avgcadence);
		 edetail->setText(10, kl->formatNumber(qs_ascent, false));
		 edetail->setText(11, kl->formatNumber(qs_descent, false));
		 edetail->setPixmap(0, KIcon(QString("history")).pixmap(16));
		 QPixmap qpx = KIcon(QString("other")).pixmap(16);
		 
		 trdetail->setText(0, qs_etime);
		 trdetail->setTextAlignment(0, Qt::AlignRight);
		 trdetail->setText(1, kl->formatNumber(qs_distance));
		 trdetail->setTextAlignment(1, Qt::AlignRight);
		 trdetail->setText(2, kl->formatNumber(qs_totdist));
		 trdetail->setTextAlignment(2, Qt::AlignRight);
		 trdetail->setText(4, qs_avgpace);
		 trdetail->setTextAlignment(4, Qt::AlignRight);
		 trdetail->setText(5, kl->formatNumber(qs_avgspeed, false));
		 trdetail->setTextAlignment(5, Qt::AlignRight);
		 trdetail->setText(6, kl->formatNumber(qs_maxspeed, false));
		 trdetail->setTextAlignment(6, Qt::AlignRight);
		 trdetail->setText(7, qs_calories);
		 trdetail->setTextAlignment(7, Qt::AlignRight);
		 trdetail->setText(8, qs_avghr);
		 trdetail->setTextAlignment(8, Qt::AlignRight);
		 trdetail->setText(9, qs_maxhr);
		 trdetail->setTextAlignment(9, Qt::AlignRight);
		 trdetail->setText(10, qs_avgcadence);
		 trdetail->setTextAlignment(10, Qt::AlignRight);
		 trdetail->setText(11, kl->formatNumber(qs_ascent, false));
		 trdetail->setTextAlignment(11, Qt::AlignRight);
		 
		 switch (rakt->run->sport_type)
		 {
		    case D1000_running:
		       edetail->setPixmap(0, KIcon(QString("spw-running")).pixmap(16));
		    break;

		    case D1000_biking:
		       edetail->setPixmap(0, KIcon(QString("bike")).pixmap(16));
		    break;

		    case D1000_other:
		       edetail->setPixmap(0, qpx);
		    break;

		    default:
		       edetail->setPixmap(0, qpx);
		 }

		 ui_sportwatcherWidgetBase.liLaps->clearSelection();
		 element->insertItem(edetail);
		 delete qt;
		 laps++;
	      }

	      qs_ascent.sprintf("%.2f %s", (Units == 1) ? sum_asc / 0.304 : sum_asc, (Units == 1) ? "ft" : "m");
	      qs_descent.sprintf("%.2f %s", (Units == 1) ? sum_dsc / 0.304 : sum_dsc, (Units == 1) ? "ft" : "m");
      	      element->setText(10, qs_ascent);
	      element->setText(11, qs_descent);
	      qs_ascent.sprintf("%s %s", (Units == 1) ? kl->formatNumber(ds.getAscend() / 0.304, 2).toAscii().data() : kl->formatNumber(ds.getAscend(), 2).toAscii().data(), (Units == 1) ? "ft" : "m");
	      ui_sportwatcherWidgetBase.edTotalHeight->clear();
	      ui_sportwatcherWidgetBase.edTotalHeight->insert(qs_ascent);
	      ui_sportwatcherWidgetBase.edLapNumber->setAlignment(Qt::AlignRight);
	      ui_sportwatcherWidgetBase.edLapNumber->insert(kl->formatNumber((double)laps, 0));
	   }

	   rakt = rakt->next;
	}

	if (men > 0)
	   avg_hr /= men;
	else
	   min_hr = max_hr = avg_hr = 0;
}

void sportwatcherWidget::showTrack()
{
	showTrack(0, QRect(0, 0, 0, 0), 0);
}

void sportwatcherWidget::showTrack(int zoom)
{
	showTrack(zoom, mapPan, mapLap);
}

void sportwatcherWidget::showTrack(int zoom, const QRect &pan, LAP *lap)
{
int width, height;
double x1, y1, x2, y2;
int a, top, left, panX, panY;
uint32 i;
double coordW, coordH, tick;
double meterW, dist, fact;
QPainter paint;
posn_type posNW, posSE, posLXY, posRXY;
POINT *point;
bool Fgeo = false;
bool Data = false;
#if defined HAVE_GDAL
QString fName = MAP;
//double adfGeoTransform[6];
GDALDataset *poDataset = 0;
GDALRasterBand *poBand = 0;
unsigned char *pafScanline = 0;
unsigned char *pafScanlineRed = 0;
unsigned char *pafScanlineGreen = 0;
unsigned char *pafScanlineBlue = 0;
unsigned char *pafScanlineAlpha = 0;
int nXSize, nYSize;
int xOff, yOff;
double oriLeftLon, oriLeftLat, oriRightLon, oriRightLat;
#endif

	if (!DIRTY || curTab == 2 || curTab == 3)
	   return;

	if (!gmn)
	   return;

	QApplication::setOverrideCursor (QCursor(Qt::WaitCursor));

	if (zoom != zfactor)
	   zfactor = zoom;

	if (mapLap != lap)
	   mapLap = lap;

	y2 = x2 = 0;
#if defined HAVE_GDAL
	nXSize = nYSize = 0;
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
	KConfigGroup wms (&cfg, "WMS");
	bool square = wms.readEntry("Square", false);
	int CorrX = wms.readEntry("CorrX", 0);
	int CorrY = wms.readEntry("CorrY", 0);
	KConfigGroup ic (&cfg, "ImageCoords");
	oriLeftLon = ic.readEntry("LeftLon", 0.0);
	oriLeftLat = ic.readEntry("LeftLat", 0.0);
	oriRightLon = ic.readEntry("RightLon", 0.0);
	oriRightLat = ic.readEntry("RightLat", 0.0);
//	int isrs = ic.readEntry("SRS", 0);
#endif
	if (curTab == 0 && !ActivePrint)
	{
	   width = ui_sportwatcherWidgetBase.imgMap->width() - 2;
	   height = ui_sportwatcherWidgetBase.imgMap->height();
	}
	else if (ActivePrint)
	{
	   width = pmPrMap.width();
	   height = pmPrMap.height();
	}
	else
	{
	   width = ui_sportwatcherWidgetBase.grMap->width() - 2;
	   height = ui_sportwatcherWidgetBase.grMap->height();
	}
#if defined HAVE_GDAL
	if (MapType == MPT_WMS && square)
	   pmMap = QPixmap(width / (int)mFactor * (int)mFactor, height / (int)mFactor * (int)mFactor);
	else
	   pmMap = QPixmap(width, height);
#else
	pmMap = QPixmap(width, height);
#endif
	if (pmMap.isNull())
	   return;

	// Here we begin do draw something
	paint.begin(&pmMap);

	panX = panY = 0;

	if (stateHand && mapPan != pan)
	{
	   mapPan = pan;
	   panX = mapPan.right() - mapPan.left();
	   panY = mapPan.bottom() - mapPan.top();
	   oldTransX += (double)panX;
	   oldTransY += (double)panY;
	}

	memset(&posNW, 0, sizeof(posn_type));
	memset(&posSE, 0, sizeof(posn_type));

	posSE.lat = 90.0;
	posSE.lon = 180.0;
	posNW.lat = -90.0;
	posNW.lon = -180.0;

	/*
	 * Find out the corners of our track (NW, NE, SE, SW)
	 */
	if (mapLap)
	   i = mapLap->start_time;
	else
	   i = 0;
	
	while ((point = ds.getPoint(i)) != 0)
	{
	   if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
	      break;

	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
	   {
	      i = point->time + 1;
	      continue;
	   }

	   if (SEMI2DEG(point->posn.lat) > posNW.lat)
	      posNW.lat = SEMI2DEG(point->posn.lat);

	   if (SEMI2DEG(point->posn.lat) < posSE.lat)
	      posSE.lat = SEMI2DEG(point->posn.lat);

	   if (SEMI2DEG(point->posn.lon) > posNW.lon)
	      posNW.lon = SEMI2DEG(point->posn.lon);

	   if (SEMI2DEG(point->posn.lon) < posSE.lon)
	      posSE.lon = SEMI2DEG(point->posn.lon);

	   i = point->time + 1;
	   Data = true;
	}

	coordW = (posNW.lon > posSE.lon) ? posNW.lon - posSE.lon : posSE.lon - posNW.lon;
	coordH = (posNW.lat > posSE.lat) ? posNW.lat - posSE.lat : posSE.lat - posNW.lat;
	meterW = ds.earth_distance(posNW.lat, posNW.lon, posNW.lat, posSE.lon);

	// define the ticks to translate the GPS coordinates into pixels.
	// The track should be centered and we have to calculate the
	// rectangular within we draw the track.
	if (coordW < coordH)
	{
	   tick = (double)width / coordW + (double)zoom;

	   if ((tick * coordH) > height)
	      tick = (double)height / coordH + (double)zoom;
	}
	else
	{
	   tick = (double)height / coordH + (double)zoom;

	   if ((tick * coordW) > width)
	      tick = (double)width / coordW + (double)zoom;
	}

	left = width - (width - tick * coordW) / 2;
	top = (height - tick * coordH) / 2;

	a = tick * coordW;

	if (Units == 0)
	   dist = meterW / a;			// Meters
	else
	   dist = meterW * 1.609344 / a;	// 1/1000 mile (5.28 feet)

#if defined HAVE_GDAL
	geoRect.llat = 0.0;
	geoRect.llon = 0.0;
	geoRect.rlat = 0.0;
	geoRect.rlon = 0.0;
	geoRect.width = width;
	geoRect.height = height;
	/*
	 * If we have a map file, we try to read it and if successfull,
	 * we should get a map painted.
	 *
	 * Currently only WMS-Server is supported, allthough GDAL allows
	 * several other formats too.
	 */
	if (!MAP.isEmpty() && Data)
	{
	bool writeTag = true;
	double mtx, mty;
	double vx, vy;

	   xOff = yOff = 0;
	   posRXY.lon = posNW.lon + (width - left + oldTransX) / tick;
	   posLXY.lat = posNW.lat + (top + oldTransY) / tick;
	   posLXY.lon = posSE.lon - (left - a - oldTransX) / tick;
	   posRXY.lat = posSE.lat - (height - top - (tick * coordH) - oldTransY) / tick;
	   geoRect.llat = posLXY.lat;
	   geoRect.llon = posLXY.lon;
	   geoRect.rlat = posRXY.lat;
	   geoRect.rlon = posRXY.lon;
	   // width and height of map in meters
	   mtx = ds.earth_distance(posRXY.lat, posRXY.lon, posRXY.lat, posLXY.lon);
	   mty = ds.earth_distance(posRXY.lat, posRXY.lon, posLXY.lat, posRXY.lon);

	   // factor to correct the map, in case we use a WMS server
	   if (MapType == MPT_WMS)
	   {
	      vx = (posRXY.lon - posLXY.lon) / mtx * CorrX;
	      vy = (posRXY.lat - posLXY.lat) / mty * CorrY;
	      posRXY.lon += vx;
	      posRXY.lat += vy;
	      posLXY.lon += vx;
	      posLXY.lat += vy;
	   }

	   /*
	    * Write a control file for GDAL, if we use a WMS server.
	    * Warp an image if we use PNG or BMP or GIF.
	    * Warp a region if we use TIFF
	    */
	   if (MapType == MPT_WMS)
	      writeTag = writeWMSTag(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat, width, height);

	   if (MapType == MPT_GIF || MapType == MPT_BMP || MapType == MPT_PNG ||
	       MapType == MPT_SGI || MapType == MPT_TIF)
	      writeTag = warpImage(MAP, &fName);

	   if (writeTag)
	   {
	      if (MapType != MPT_SHP && (poDataset = (GDALDataset *)GDALOpen (fName.toAscii().constData(), GA_ReadOnly)) != NULL)
	      {
		 QPixmap bild;
		 int nRasterCount = poDataset->GetRasterCount();
		 int nXBlock, nYBlock;
		 GDALColorTable *pCT, *pCTb, *pCTr, *pCTg, *pCTa;

		 int             bGotMin, bGotMax;
		 int		 tTypeLen, tColor, tColorEntrys;
		 GDALDataType    tRasterType;
		 double          adfMinMax[2];

		 pCT = pCTb = pCTr = pCTg = pCTa = 0;
		 tTypeLen = 0;
		 pafScanlineRed = pafScanlineGreen = pafScanlineBlue = pafScanlineAlpha = 0;
		 Fgeo = true;
		 /*
		  * Read every raster band.
		  *
		  * If we get 3 raster bands, the image is a 24 bit image.
		  * If we get 4 raster bands, the image is probably a 24 bit
		  * image with an alpha channel. Currently the alpha channel
		  * is ignored!
		  * If we have 1 raster band, the image is 8 bit monochrom.
		  * Otherwise the image is undefined and the results are also.
		  */
		 for (a = 1; a <= nRasterCount; a++)
		 {
		    if (!Fgeo)
		       break;

		    if (!(poBand = poDataset->GetRasterBand (a)))
		    {
		       paint.end();
		       return;
		    }

		    poBand->GetBlockSize (&nXBlock, &nYBlock);
		    nXSize = poBand->GetXSize();
		    nYSize = poBand->GetYSize();
		    tRasterType = poBand->GetRasterDataType ();
		    tTypeLen = GDALGetDataTypeSize (tRasterType) / 8;	// We need Bytes not Bits!
		    tColor = poBand->GetColorInterpretation ();

		    adfMinMax[0] = poBand->GetMinimum (&bGotMin);
		    adfMinMax[1] = poBand->GetMaximum (&bGotMax);

		    if (!(bGotMin && bGotMax))
		       GDALComputeRasterMinMax ((GDALRasterBandH)poBand, TRUE, adfMinMax);

		    if ((pCT = poBand->GetColorTable()) != NULL)
		       tColorEntrys = poBand->GetColorTable()->GetColorEntryCount();

		    switch (a)
		    {
		       case 1: pafScanlineRed   = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineRed; pCTr = pCT; break;
		       case 2: pafScanlineGreen = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineGreen; pCTg = pCT; break;
		       case 3: pafScanlineBlue  = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineBlue; pCTb = pCT; break;
		       case 4: pafScanlineAlpha  = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineAlpha; pCTa = pCT; break;
		    }

		    if (!pafScanline)
		    {
		       paint.end();
		       KMessageBox::error(this, i18n("Not enough memory for a raster operation!"));
		       return;
		    }

		    memset (pafScanline, 0, tTypeLen * nXSize * nYSize);

		    /*
		     * Get the image (from the server) and put the tiles together.
		     *
		     * The function reads only one raster band. This is,
		     * because the function is called for every raster band and
		     * every raster band is stored into a separate array.
		     */
		    if (poBand->RasterIO (GF_Read, 0, 0, nXSize, nYSize, pafScanline, nXSize, nYSize, tRasterType, 0, 0) == CE_Failure)
		    {
		       paint.end();
		       KMessageBox::error(this, i18n("Error reading a raster band!"));
		       paint.begin(&pmMap);
		       Fgeo = false;
		       break;
		    }
		    else
		       Fgeo = true;
		 }

		 /*
		  * Only if Fgeo is TRUE, we've read successfully all raster
		  * bands. Now we have to put the bands together to get
		  * an image.
		  */
		 if (Fgeo)
		 {
		 unsigned char *pCombinedBytes = new unsigned char[(tTypeLen * nXSize * nYSize * nRasterCount)];
		 unsigned char *ptr_dest, *ptr_src;
		 int j;

		    ptr_dest = ptr_src = 0;

		    /*
		     * We need two nested loops to set the pixels in the wanted
		     * order.
		     */
		    for (a = 0, j = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount, j++)
		    {
		       int k = a;

		       for (int m = nRasterCount - 1; m >= 0; m--, k++)
		       {
		       unsigned char *pBytes = 0;

			  switch (m)
			  {
			     case 3: pBytes = pafScanlineAlpha; pCT = pCTa; break;
			     case 2: pBytes = pafScanlineBlue; pCT = pCTb; break;
			     case 1: pBytes = pafScanlineGreen; pCT = pCTg; break;
			     default: pBytes = pafScanlineRed; pCT = pCTr;
			  }

			  ptr_dest = pCombinedBytes + k;
			  unsigned char b = pBytes[j];

			  /*
			   * If we have a color table, the pixels are pointers
			   * to the color table. We need to convert them into
			   * 24 bit pixels plus an optional alpha channel.
			   */
			  if (pCT != NULL)
			  {
			     GDALColorEntry ce;
			     unsigned int c = (unsigned int)b;
			     c = pCT->GetColorEntryAsRGB (c, &ce);

			     if  (m == 0) c = ce.c1;
			     if  (m == 1) c = ce.c2;
			     if  (m == 2) c = ce.c3;
			     if  (m == 3) c = ce.c4;

			     b = (unsigned char)c;
			  }

			  ptr_src = &b;
			  memcpy (ptr_dest, ptr_src, 1);
		       }
		    }

		    x1 = y1 = 0;

		    /*
		     * The following loop is QT specific! It sets the pixels
		     * of the raw image, pixel by pixel. This may be slow, but
		     * everything else didn't work :-(
		     *
		     * FIXME: We need a more effective routine to put the
		     *        raw image into QT's "painter" class.
		     */
		    for (a = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount)
		    {
		       if (x1 < width && y1 < height)
		       {
			  if (nRasterCount == 3)
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a]), Qt::SolidLine));
			  else if (nRasterCount > 3)
			     paint.setPen (QPen(QColor(qRgba((int)pCombinedBytes[a+3], (int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a])), Qt::SolidLine));
			  else if (nRasterCount == 2)
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a+1], (int)pCombinedBytes[a], (int)pCombinedBytes[a+1]), Qt::SolidLine));
			  else if (nRasterCount == 1)
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a], (int)pCombinedBytes[a], (int)pCombinedBytes[a]), Qt::SolidLine));

			  paint.drawPoint(x1, y1);
		       }

		       x1++;

		       if (x1 >= nXSize)
		       {
			  x1 = 0;
			  y1++;
		       }
		    }

		    delete pCombinedBytes;
		    pCombinedBytes = 0;
		 }

		 if (pafScanlineRed)
		    delete pafScanlineRed;

		 if (pafScanlineGreen)
		    delete pafScanlineGreen;

		 if (pafScanlineBlue)
		    delete pafScanlineBlue;

		 if (pafScanlineAlpha)
		    delete pafScanlineAlpha;

		 GDALClose (poDataset);
		 poDataset = 0;

		 if (MAP != fName)
		    unlink (fName.toAscii().data());
	      }
#if defined HAVE_MAPNIK
	      else if (MapType == MPT_SHP)
	      {
		 QDir shpd(MAP, QString("*.shp"));

		 if (shpd.count() < 1)
		 {
		    KMessageBox::error(this, i18n("There is no shape file in directory %1").arg(MAP));
		    Fgeo = false;
		 }
		 else
		 {
		 SRender rd;
		 QColor bg(220, 220, 220);		// background color

		    if (curTab == 0 && !ActivePrint)
		       rd.setDrawArea(ui_sportwatcherWidgetBase.imgMap->width(), ui_sportwatcherWidgetBase.imgMap->height());
		    else if (!ActivePrint)
		       rd.setDrawArea(ui_sportwatcherWidgetBase.grMap->width(), ui_sportwatcherWidgetBase.grMap->height());
		    else if (ActivePrint)
		       rd.setDrawArea(pmPrMap.width(), pmPrMap.height());

		    rd.setMapType(SRender::MAP_SHAPE);

		    if (rd.getMap(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat))
		    {
		       paint.fillRect(0, 0, width+2, height+2, bg);
		       paint.drawPixmap(0, 0, rd.pixmap());
		       Fgeo = true;
		    }
		    else
		       Fgeo = false;
		 }
	      }
	      else if (MapType == MPT_OSM)
	      {
		 QFileInfo osmf(MAP);

		 if (!osmf.exists())
		 {
		    KMessageBox::error(this, i18n("The OSM file %1 does not exist!").arg(MAP));
		    Fgeo = false;
		 }
		 else
		 {
		 SRender rd;
		 QColor bg(220, 220, 220);		// background color

		    if (curTab == 0 && !ActivePrint)
		       rd.setDrawArea(ui_sportwatcherWidgetBase.imgMap->width(), ui_sportwatcherWidgetBase.imgMap->height());
		    else if (!ActivePrint)
		       rd.setDrawArea(ui_sportwatcherWidgetBase.grMap->width(), ui_sportwatcherWidgetBase.grMap->height());
		    else if (ActivePrint)
		       rd.setDrawArea(pmPrMap.width(), pmPrMap.height());

		    rd.setMapType(SRender::MAP_OSM);

		    if (rd.getMap(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat))
		    {
		       paint.fillRect(0, 0, width+2, height+2, bg);
		       paint.drawPixmap(0, 0, rd.pixmap());
		       Fgeo = true;
		    }
		    else
		       Fgeo = false;
		 }
	      }
#endif	// HAVE_MAPNIK
	      else
	      {
		 KMessageBox::error(this, i18n("Error opening map file!"));
		 Fgeo = false;
	      }
	   }
	}
#endif	// HAVE_GDAL
	/*
	 * Here we come to draw the track. It will be drawn over the previous
	 * created map image.
	 */
	// Colors and fonts
	QColor background(220, 220, 220);		// background color
	QColor red(255, 0, 0);				// mile marker
	QColor black(0, 0, 0);				// Text, center of track
//	QColor yellow(255, 255, 0);
	QColor yellow(0x00cf, 0x00ff, 0x0000);		// color of track
	QFont fntNormal("Helvetica");
	fntNormal.setPixelSize(10);
	fntNormal.setStyleHint(QFont::Helvetica);
	QPen dot(red, 4, Qt::SolidLine);
	QPen line(black, 2, Qt::SolidLine);
	QPen yline(yellow, 4, Qt::SolidLine);
	// Fill background with background colors, if there is no map.
	if (!Fgeo)
	   paint.fillRect(0, 0, width+2, height+2, background);

	if (Units == 0)
	   fact = 1000.0;
	else
	   fact = 1609.344;

	paint.setPen(line);
	paint.drawLine(10, height - 9, 10, height - 4);
	paint.drawLine(10, height - 4, 10 + (fact / dist), height - 4);
	paint.drawLine(10 + (fact / dist), height - 9, 10 + (fact / dist), height - 4);
	paint.setFont(fntNormal);

	if (Units == 0)
	   paint.drawText(10, height - 10, QString("1000 m"));
	else
	   paint.drawText(10, height - 10, QString("5280 ft"));

	// Draw track
	if (mapLap)
	   i = mapLap->start_time;
	else
	   i = 0;
	
	x1 = y1 = 0.0;
	bool wStart = false;

	while ((point = ds.getPoint(i)) != 0)
	{
	   if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
	      break;

	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
	   {
	      i = point->time + 1;
	      continue;
	   }

	   x2 = (left + ((posNW.lon - SEMI2DEG(point->posn.lon)) * tick * -1)) + oldTransX;
	   y2 = (top + ((posNW.lat - SEMI2DEG(point->posn.lat)) * tick)) + oldTransY;

	   if (!wStart && x1 != 0.0 && y1 != 0.0)
	   {
	      // Load the start symbol
	      QPixmap qpx (KIcon(QString("wstart")).pixmap(16));
	      // Find the angle of the track and turn the symbol accordingly
	      // we use Pythagoras to calculate the triangle
	      double xl = (x1 < x2) ? x2 - x1 : x1 - x2;
	      double yl = (y1 < y2) ? y2 - y1 : y1 - y2;
	      double da = fmin (xl, yl);
	      double db = fmax (xl, yl);
	      double zl = sqrt (pow (da, 2) + pow (db, 2));
	      double angle = (asin(da / zl) / M_PIl) * 180.0;

	      angle = (angle > 45.0) ? 90.0 - angle : angle;
// cout << "Winkel: " << angle << " ---- X: " << xl << ", Y: " << yl << ", Z: " << zl << ", Point (x1,y1,x2,y2): " << x1 << ", " << y1 << ", " << x2 << ", " << y2 << endl;
	      if (x1 < x2 && y1 < y2)		// right, down
		 angle = 90.0 + angle;
	      else if (x1 > x2 && y1 < y2)	// left, down
		 angle = 270.0 - angle;
	      else if (x1 > x2 && y1 > y2)	// left, up
		 angle = 270.0 + angle;
	      else				// right, up
		 angle = 90.0 - angle;
// cout << "Realer Winkel: " << angle << endl;
	      // Set the center of the symbol
	      paint.save ();
	      paint.translate (x1, y1);
	      // rotate the symbol
	      paint.rotate (angle);
	      paint.drawPixmap (-8, -8, qpx);
	      paint.restore ();
	      wStart = true;
	   }

	   if (x1 == 0.0 && y1 == 0.0)
	   {
	      x1 = x2;
	      y1 = y2;
	   }

	   paint.setPen(yline);
	   paint.drawLine(x1, y1, x2, y2);

	   if ((point->distance - dist) >= fact)	// a dot at every 1000 meters or at 1 mile
	   {
	      paint.setPen(dot);
	      paint.drawEllipse(x2-2, y2-2, 3, 3);
	      dist = (int)(point->distance / fact) * fact;
	   }

	   paint.setPen(line);
	   paint.drawLine(x1, y1, x2, y2);
	   x1 = x2;
	   y1 = y2;
	   i = point->time + 1;
	}

	bool lastLap = false;

	if (mapLap)
	   if (ds.getRunNode()->run->last_lap_index == mapLap->index)
	      lastLap = true;

	if ((!mapLap || lastLap) && wStart)
	{
	   // load the end symbol
	   QPixmap qpx (KIcon(QString("wtarget")).pixmap(16));
	   paint.drawPixmap (x2, y2 - 16, qpx);
	}

	paint.end();

	if (curTab == 0 && !ActivePrint)
	   ui_sportwatcherWidgetBase.imgMap->setPixmap(pmMap);
	else if (ActivePrint)
	   pmPrMap = pmMap;
	else
	   ui_sportwatcherWidgetBase.grMap->setPixmap(pmMap);

	QApplication::restoreOverrideCursor();
}

void sportwatcherWidget::kcbCurveSlot(int)
{
	DIRTY = true;
	showCurves();
	DIRTY = false;
}

void sportwatcherWidget::tabViewSlot(int tab)
{
	curTab = tab;

	if (tab == 0 && tabDirt0)
	{
	   DIRTY = true;
	   showLaps();
	   showTrack();
	   showCurves();
	   DIRTY = false;
	   tabDirt0 = false;
	}
	else if (tab == 1 && tabDirt1)
	{
	   DIRTY = true;

	   if (tabDirt0)
	      showLaps();

	   showTrack();
	   DIRTY = false;
	   tabDirt1 = false;
	}
	else if (tab == 2 && tabDirt2)
	{
	   DIRTY = true;

	   if (tabDirt0)
	      showLaps();

	   setMouseTracking (true);
	   ui_sportwatcherWidgetBase.tabView->setMouseTracking (true);
	   ui_sportwatcherWidgetBase.grHR->setMouseTracking (true);
	   ui_sportwatcherWidgetBase.grElevation->setMouseTracking (true);
	   ui_sportwatcherWidgetBase.grSpeed->setMouseTracking (true);
	   showThreeCurve();
	   tabDirt2 = false;
	   DIRTY = false;
	}
}

void sportwatcherWidget::showCurves()
{
	showCurves (mapLap);
}

void sportwatcherWidget::showCurves(LAP *lap)
{
QPainter paint;
int width, height;
int i, secs, cuType;
int lineHeight, margin_left, margin_right, margin_bottom;
int x1, y1, x2, y2;		// Coordinates
bool meter;
double maxHeight, minHeight, maxSpeed, minSpeed;
int maxHr, minHr, rh;
POINT *point;
RUN_NODE *rn;
LAP *lp;
double w_tick, h_tick;		// Number of pixels one "tick" has;
				// This depends on the width and height
				// of the image.
	// First we draw a grid based on the min and max
	// values detected in the function showLap(). In case
	// all values are 0, we exit here.
	if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
	   return;

	if (!DIRTY || curTab != 0)
	   return;

	// Look up, what curves we should draw
	cuType = ui_sportwatcherWidgetBase.kcbCurveTypes->currentIndex();
	// Get the dimensions of the available draw area
	width = ui_sportwatcherWidgetBase.imgProfile->width() - 2;
	height = ui_sportwatcherWidgetBase.imgProfile->height();
	pmProfile = QPixmap(width, height);
	paint.begin(&pmProfile);

	// we need a somewhat bigger area to draw our curves than
	// we have with the real min and max values.
	if (max_height > 0.0)
	{
	double add = (max_height - min_height) / 100.0 * 5.0;	// Percent

	   maxHeight = max_height + add;
	   minHeight = min_height - add;

	   if (minHeight < 0.0)		// make sure, we are not too deep
	      minHeight = 0.0;
	}
	else
	   maxHeight = minHeight = 0.0;

	if (max_speed > 0.0)
	{
	double add = (max_speed - min_speed) / 100.0 * 5.0;	// Percent

	   maxSpeed = max_speed + add;
	   minSpeed = min_speed - add;

	   if (minSpeed < 0.0)		// make sure, we are not too deep
	      minSpeed = 0.0;
	}
	else
	   maxSpeed = minSpeed = 0.0;

	if (max_hr > 0)
	{
	   maxHr = max_hr + 10;
	   minHr = min_hr - 10;

	   if (minHr < 0)
	      minHr = 0;
	}
	else
	   maxHr = minHr = 0;

	// Define colors
	QColor background(220, 220, 220);	// Background of graphic
	QColor mark(255, 255, 255);		// Lines inside curve area
	QColor hlight(180, 180, 180);		// area of current lap
//	hlight.setAlpha(128);			// 50% transparent
	QColor frame(0, 0, 0);			// Text and borders
	QColor barcol(151, 190, 13);		// heart rate
	QColor barcol2(190, 151, 13);		// height over NN
	QColor red(220, 128, 128);		// speed
	QColor blue(0, 0, 240);
	QFont fntNormal("Helvetica");
//	QFont fntBold("Helvetica", 10, QFont::Bold);
	fntNormal.setPixelSize(10);
	fntNormal.setStyleHint(QFont::Helvetica);
//	fntBold.setPixelSize(10);
//	fntBold.setStyleHint(QFont::Helvetica);
	// Calculate ticks
	margin_left = 52;
	margin_right = 40;
	margin_bottom = 12;
	lineHeight = 10;
	rh = height - margin_bottom - 1;

	w_tick = (double)(width - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second

	if (cuType == 1)	// Speed and heart rate?
	{
	   if ((maxSpeed - minSpeed) > (double)(maxHr - minHr))
	   {
	      h_tick = (double)rh / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
	      meter = true;
	   }
	   else
	   {
	      h_tick = (double)rh / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
	      meter = false;
	   }
	}
	else if (cuType == 2)	// Elevation and speed?
	{
	   if ((maxHeight - minHeight) > (double)(maxHr - minHr))
	   {
	      h_tick = (double)rh / (maxHeight - minHeight);		// 1 tick = 1 meter
	      meter = true;
	   }
	   else
	   {
	      h_tick = (double)rh / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
	      meter = false;
	   }
	}
	else			// Elevation and heart rate
	{
	   if ((maxHeight - minHeight) > (double)(maxHr - minHr))
	   {
	      h_tick = (double)rh / (maxHeight - minHeight);		// 1 tick = 1 meter
	      meter = true;
	   }
	   else
	   {
	      h_tick = (double)rh / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
	      meter = false;
	   }
	}

	// Fill background with background colors
	paint.fillRect(0, 0, width + 4, height + 4, background);
	// Draw a grid with markers at every 10 minutes
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
	// Bottom border line
	x1 = margin_left;
	y1 = height - margin_bottom;
	x2 = width - margin_right;
	y2 = y1;
	paint.drawLine(x1, y1, x2, y2);
	// Left border line
	x1 = x2 = margin_left;
	y1 = 2;
	y2 = height - margin_bottom;
	paint.drawLine(x1, y1, x2, y2);
	// right border line
	x1 = x2 = width - margin_right;
	paint.drawLine(x1, y1, x2, y2);

	// Draw some darker lines to show the laps, if we have one
	// and, in case we have a given lap, we fill the area.
	QDateTime *qt;
	QTime zeit = StartTime.time();
	rn = ds.getRunNode();
	paint.setPen(QPen(hlight, 1, Qt::SolidLine));

	for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
	{
	   if ((lp = ds.getLap(i)) == NULL)
	      continue;

	   qt = garmin_dtime(lp->start_time);
	   secs = zeit.secsTo(qt->time());
	   delete qt;
	   x1 = secs * w_tick + margin_left + 1;

	   if (lap && lp->start_time == lap->start_time)
	      paint.fillRect(x1, 2, (int)((double)lap->total_time / 100.0 * w_tick), height - margin_bottom - 2, hlight);
	   else
	      paint.drawLine(x1, 2, x1, height - margin_bottom);
	}

	// Grid vertical
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
	paint.setFont(fntNormal);
	paint.drawText(margin_left - 20, height - lineHeight, 40, lineHeight, Qt::AlignCenter, QString("00:00"));
	paint.save();
	paint.rotate(270);

	if (cuType == 1)
	   paint.setPen(QPen(red, 1, Qt::SolidLine));
	else
	   paint.setPen(QPen(barcol, 1, Qt::SolidLine));

	// Information on left side
	if (cuType == 0)
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n("Elevation (%1)").arg((Units == 1) ? "ft" : "m"));
	else if (cuType == 1)
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n("Speed (%1)").arg((Units == 1) ? "mph" : "km/h"));
	else
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n("Elevation (%1)").arg((Units == 1) ? "ft" : "m"));

	if (cuType == 2)
	   paint.setPen(QPen(red, 1, Qt::SolidLine));
	else
	   paint.setPen(QPen(blue, 1, Qt::SolidLine));

	// Information on right side
	if (cuType < 2)
	   paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
	else
	   paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n("Speed (%1)").arg((Units == 1) ? "mph" : "km/h"));

	paint.restore();
	paint.setPen(QPen(mark, 1, Qt::SolidLine));
	// Draw the time scale on the bottom of the graphic
	for (i = 0; (unsigned int)i < (max_time + ds.getPauseTime()); i++)
	{
	   if (i > 0 && !(i % 600))	// every 10 minutes
	   {
	      x1 = x2 = margin_left + w_tick * i;

	      if (x1 == (width - margin_right))
		 continue;

	      y1 = 2;
	      y2 = height - margin_bottom;
	      paint.drawLine(x1, y1, x2, y2);
	      QTime tm(0, 0, 0);
	      tm = tm.addSecs(i);
	      paint.setPen(QPen(frame, 1, Qt::SolidLine));
//	      paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((i >= 3600) ? "hh:mm:ss" : "mm:ss"));
	      paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
	      paint.setPen(QPen(mark, 1, Qt::SolidLine));
	   }
	}

	// This is the total time, with pauses included, at the lower right
	// corner of the graphic.
	QTime tm(0, 0, 0);
	QString qs;
	tm = tm.addSecs(max_time + ds.getPauseTime());
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
//	paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((max_time >= 3600) ? "hh:mm:ss" : "mm:ss"));
	paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));

	// Draw the minimal elevation, speed and/or heart rate
	if (max_height > 0.0 || max_speed > 0.0)
	{
	   // left side
	   if (cuType == 1)
	   {
	      paint.setPen(QPen(red, 1, Qt::SolidLine));
	      paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minSpeed));
	   }
	   else
	   {
	      paint.setPen(QPen(barcol, 1, Qt::SolidLine));
	      paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
	   }
	}
	
	if (max_hr > 0 || max_speed > 0.0)
	{
	   // right side
	   if (cuType == 2)
	   {
	      paint.setPen(QPen(red, 1, Qt::SolidLine));
	      paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", minSpeed));
	   }
	   else
	   {
	      paint.setPen(QPen(blue, 1, Qt::SolidLine));
	      paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
	   }
	}

	paint.setPen(QPen(mark, 1, Qt::SolidLine));

	// Grid horizontal
	int factor = 0;
	int target = 0;

	if (cuType == 0)	// Elevation and heart rate
	{
	   factor = (meter) ? (maxHeight - minHeight) / (rh / 12) : (maxHr - minHr) / (rh / 12);
	   target = (meter) ? (int)(maxHeight - minHeight) : (maxHr - minHr);
	}
	else if (cuType == 1)	// Speed and heart rate
	{
	   factor = (meter) ? (maxSpeed - minSpeed) / (rh / 12) : (maxHr - minHr) / (rh / 12);
	   target = (meter) ? (int)(maxSpeed - minSpeed) : (maxHr - minHr);
	}
	else			// Elevation and speed
	{
	   factor = (meter) ? (maxHeight - minHeight) / (rh /12) : (maxSpeed - minSpeed) / (rh / 12);
	   target = (meter) ? (int)(maxHeight - minHeight) : (int)(maxSpeed - minSpeed);
	}

	// To prevent a division by zero error, we check the <factor>
	if (factor == 0)
	   factor = 1;

	// Beside the horizontal part of the grid, we draw the scale on the
	// left and the right side.
	int oldy = height;

	for (i = 0; i < target; i++)
	{
	   if (i > 0 && !(i % factor))
	   {
	      x1 = margin_left + 1;
	      x2 = width - margin_right - 1;
	      y1 = y2 = rh - h_tick * i;

	      if (y1 < 12)
		 break;

	      paint.drawLine(x1, y1, x2, y2);

	      if (y1 < (oldy - lineHeight))
	      {
		 if (meter)
		 {
		    paint.setPen(QPen(barcol, 1, Qt::SolidLine));
		    // left side
		    if (cuType == 1)
		    {
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + i));
		    }
		    else
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));

		    // right side
		    if (maxHr > 0 && cuType != 2)
		    {
		       double hrscale = (double)(maxHr - minHr) / (double)target;
		       paint.setPen(QPen(blue, 1, Qt::SolidLine));
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", (int)((double)minHr + hrscale * (double)i)));
		    }
		    else
		    {
		       double spscale = (maxSpeed - minSpeed) / (double)target;

		       if (cuType == 2)
			  paint.setPen(QPen(red, 1, Qt::SolidLine));
		       else
			  paint.setPen(QPen(blue, 1, Qt::SolidLine));

		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", (minSpeed + spscale * (double)i)));
		    }
		 }
		 else
		 {
		    // right side
		    if (cuType == 2)
		    {
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", minSpeed + i));
		    }
		    else
		    {
		       paint.setPen(QPen(blue, 1, Qt::SolidLine));
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
		    }

		    // left side
		    if ((cuType == 0 || cuType == 2) && max_height > 0)
		    {
		       double hrscale = (maxHeight - minHeight) / (double)target;
		       paint.setPen(QPen(barcol, 1, Qt::SolidLine));
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight + hrscale * (double)i));
		    }
		    else if (max_speed > 0 && cuType == 1)
		    {
		       double hrscale = (maxSpeed - minSpeed) / (double)target;
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + hrscale * (double)i));
		    }
		}

		 paint.setPen(QPen(mark, 1, Qt::SolidLine));
		 oldy = y1;
	      }
	   }
	}

	// To make our graphics more beautiful, we draw lines for the
	// heart rate limits and the average heart rate.
	if (max_hr > 0 && cuType != 2)
	{
	int ay1, ay2, ay3, ay4, ay5;

	   x1 = margin_left + 1;
	   x2 = width - margin_right - 1;

	   if (meter)
	   {
	      double hrscale = rh / (double)(maxHr - minHr);
	      ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
	      ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
	      ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
	      ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
	      ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
	   }
	   else
	   {
	      ay1 = (double)rh - (double)(lower1 - minHr) * h_tick;
	      ay2 = (double)rh - (double)(lower2 - minHr) * h_tick;
	      ay3 = (double)rh - (double)(lower3 - minHr) * h_tick;
	      ay4 = (double)rh - (double)(upper3 - minHr) * h_tick;
	      ay5 = (double)rh - (double)(avg_hr - minHr) * h_tick;
	   }

	   paint.setPen(QPen(barcol2, 1, Qt::DashLine));	// color for limits

	   if (lower1 > minHr && lower1 < maxHr)
	      paint.drawLine(x1, ay1, x2, ay1);

	   if (lower2 > minHr && lower2 < maxHr)
	      paint.drawLine(x1, ay2, x2, ay2);

	   if (lower3 > minHr && lower3 < maxHr)
	      paint.drawLine(x1, ay3, x2, ay3);

	   if (upper3 > minHr && upper3 < maxHr)
	      paint.drawLine(x1, ay4, x2, ay4);

	   paint.setPen(QPen(red, 1, Qt::DashDotLine));	// color for average heart rate

	   if (avg_hr > minHr && avg_hr < maxHr)
	      paint.drawLine(x1, ay5, x2, ay5);
	}

	// Now we have a grid and we've done the scaling.
	// This is the point where we draw the curves itself.
	// We use different colors to draw the lines:
	//
	// Green: Elevation
	// Red:   Speed
	// Blue   Heart Rate
	//
	x1 = x2 = y1 = y2 = 0;
	int hy1, hy2, hx1, hx2;
	int sy1, sy2, sx1, sx2;
	hy1 = hy2 = hx1 = hx2 = 0;
	sy1 = sy2 = sx1 = sx2 = 0;
	int hEc = 0;
	i = 0;
	AVGHEIGHT *avgHakt, *avgHfirst, *avgHlast, *avgHeight = 0;
	avgHfirst = avgHlast = 0;
	// To even the surface lines, we store every altitude in the
	// memory, by building a chain. Then, if the user has set in the
	// settings (Contour == true), we will even the line by calculating
	// the average of 10 messure points and setting every value to the
	// average, who is up or down more than 2 meters from the average.
	while ((point = ds.getPoint(i)) != 0)
	{
	   if (point->alt > 20000.0 || point->alt < -1000.0)
	   {
	      i++;
	      continue;
	   }

	   if (!avgHeight)
	   {
	      avgHeight = new AVGHEIGHT;
	      avgHeight->alt = point->alt;
	      avgHeight->pos = hEc;
	      avgHeight->prev = 0;
	      avgHeight->next = 0;
	      avgHakt = avgHeight;
	      avgHfirst = avgHeight;
	   }
	   else
	   {
	      avgHakt = new AVGHEIGHT;
	      avgHakt->alt = point->alt;
	      avgHakt->pos = hEc;
	      avgHakt->next = 0;
	      avgHakt->prev = avgHeight;
	      avgHeight->next = avgHakt;
	      avgHeight = avgHakt;
	   }

	   // FIXME: Currently we can not draw below 0 meters, because the
	   // base line is always 0!
	   if (avgHakt->alt < minHeight)
	      avgHakt->alt = minHeight;

	   hEc++;
	   i++;
	}

	avgHlast = avgHeight;
	// If wanted, even the lines
	if (Contour && hEc > 0 && cuType != 0)
	{
	double alt[100], avg, avg1, avg2, avg3, avg4;
	int a, pos;

	   for (i = 0; i < (hEc + 100); i += 100)
	   {
	      avg = avg1 = avg2 = avg3 = avg4 = 0.0;
	      pos = 0;

	      for (a = 0; a < 100; a++)
	      {
		 alt[a] = getAvgAlt(avgHfirst, i + a);
		 avg += alt[a];

		 if (a < 25)
		    avg1 += alt[a];
		 else if (a < 50)
		    avg2 += alt[a];
		 else if (a < 75)
		    avg3 += alt[a];
		 else
		    avg4 += alt[a];
	      }

	      if ((i + 100) >= hEc)
		 avg /= (double)(hEc - i) + 1.0;
	      else
		 avg /= 100.0;

	      for (a = 0; a < 100; a++)
	      {
		 if ((avgHakt = getAvgPtr(avgHfirst, i + a)) != 0)
		 {
		    if ((avgHakt->alt - avg) > 2 || (avgHakt->alt - avg) < -2)
		       avgHakt->alt = avg;
		 }
	      }
	   }
	}

	// plot the elevation, speed and/or heart rate. Depends on <cuType>)
	i = 0;
	int j = 0;
	POINT *oldPoint = 0;
	double speed = 0.0;	// calculated speed
	bool pause = false;	// filter pause out of speed
	unsigned long t1, t2;
	t1 = t2 = 0;
	
	while ((point = ds.getPoint(i)) != 0)
	{
	   if (!oldPoint)
	      oldPoint = point;

	   // calculate the y position based on the time
	   qt = garmin_dtime(point->time);
	   secs = zeit.secsTo(qt->time());
	   delete qt;
	   x2 = secs * w_tick + margin_left + 1;
	   hx2 = x2;
	   sx2 = x2;

	   if (x1 == 0)
	      x1 = x2;

	   if (hx1 == 0)
	      hx1 = hx2;

	   if (sx1 == 0)
	      sx1 = sx2;

	   // The speed is not very exact, because smallest time is
	   // one second. This allows a maximum error of 99 hundredths
	   // of a second, what is very close to one second. Because of
	   // this, speed seems to hop for every messure point. This
	   // looks ugly, but currently I don't know how to make it
	   // better.
	   if (cuType == 1 || cuType == 2)	// Draw speed?
	   {
	      double dist;
	      double sc;

	      if (!pause && point->distance > 1.0e10)
	      {
		 pause = true;
		 t1 = point->time;
	      }
	      else if (pause)
	      {
		 pause = false;
		 t2 = point->time;
		 i += 2;
		 continue;
	      }

	      if (point->distance >= 0.1 && point->distance < 1.0e10)
	      {
		 dist = point->distance - oldPoint->distance;
		 sc = point->time - oldPoint->time;
		 LAP *runde = ds.getLapT (point->time);

		 if (t2 > t1)
		 {
		    sc -= t2 - t1;

		    if (sc <= 0.0)
		       sc = 1.0;		// at least 1 second!
		 }

		 speed = (dist / sc) * 3.6;

		 if (runde && runde->max_speed > 0.0 && speed > (runde->max_speed * 3.6))
		    speed = runde->max_speed * 3.6;

		 if (Units == 1)
		    speed /= 1.609344;

		 if (speed < minSpeed || speed > 400.0)
		    speed = minSpeed;

		 if ((meter && cuType == 1) || (!meter && cuType == 2))
		    y2 = (double)rh - (speed - minSpeed) * h_tick;
		 else
		 {
		    double hrscale = rh / (maxSpeed - minSpeed);
		    y2 = (double)rh - (speed - minSpeed) * hrscale;
		 }

		 if (y1 == 0)
		    y1 = y2;

		 paint.setPen(QPen(red, 1, Qt::SolidLine));
		 paint.drawLine(x1, y1, x2, y2);
		 y1 = y2;
		 x1 = x2;
		 t1 = t2 = 0;
		 oldPoint = point;
	      }
	   }

	   if (cuType == 0 || cuType == 2)		// Draw elevation?
	   {
	      if (point->alt < 20000.0 && point->alt > -1000.0)
	      {
	      double alt = getAvgAlt(avgHfirst, j);

		 j++;

		 if (meter)
		    sy2 = (double)rh - (alt - minHeight) * h_tick;
		 else
		 {
		    double hrscale = rh / (maxHeight - minHeight);
		    sy2 = (double)rh - (alt - minHeight) * hrscale;
		 }

		 if (sy1 == 0)
		    sy1 = sy2;

		 paint.setPen(QPen(barcol, 1, Qt::SolidLine));
		 paint.drawLine(sx1, sy1, sx2, sy2);
		 sy1 = sy2;
		 sx1 = sx2;
	      }
	   }

	   if (point->heart_rate > 0 && cuType < 2)	// Draw heart rate?
	   {
	      if (meter)
	      {
		 double hrscale = rh / (double)(maxHr - minHr);
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * hrscale;
	      }
	      else
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * h_tick;

	      if (hy1 == 0)
		 hy1 = hy2;

	      paint.setPen(QPen(blue, 1, Qt::SolidLine));
	      paint.drawLine(hx1, hy1, hx2, hy2);
	      hy1 = hy2;
	      hx1 = hx2;
	   }

	   i++;
	}

	paint.end();
	ui_sportwatcherWidgetBase.imgProfile->setPixmap(pmProfile);

	// free the chain of altitudes
	avgHakt = avgHfirst;

	while (avgHakt)
	{
	   avgHeight = avgHakt->next;
	   delete avgHakt;
	   avgHakt = avgHeight;
	}

	DIRTY = false;
}

void sportwatcherWidget::showThreeCurve(int pw, int ph)
{
QPainter ptHR, ptElevation, ptSpeed;
int width, height, wdHR, htHR, wdElev, htElev, wdSpeed, htSpeed;
int i, secs;
int lineHeight, margin_left, margin_right, margin_bottom;
int x1, y1, x2, y2;		// Coordinates
double maxHeight, minHeight, maxSpeed, minSpeed;
int maxHr, minHr, rh, rhHR, rhElev, rhSpeed;
POINT *point;
RUN_NODE *rn;
LAP *lp;
double wtiHR, htiHR, wtiElev, htiElev, wtiSpeed, htiSpeed;
double w_tick, h_tick;		// Number of pixels one "tick" has;
				// This depends on the width and height
				// of the image.
	// First we draw a grid based on the min and max
	// values detected in the function showLap(). In case
	// all values are 0, we exit here.
	if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
	   return;

	if (!ActivePrint && (!DIRTY || curTab != 2))
	   return;

	w_tick = h_tick = 0.0;
	rh = 0;
	width = height = 0;
	// Get the dimensions of the available draw area
	// First for heart rate
	if (!ActivePrint)
	{
	   wdHR = ui_sportwatcherWidgetBase.grHR->width() - 2;
	   htHR = ui_sportwatcherWidgetBase.grHR->height();
	   pmHR = QPixmap(wdHR, htHR);
	   // Then for elevation
	   wdElev = ui_sportwatcherWidgetBase.grElevation->width() - 2;
	   htElev = ui_sportwatcherWidgetBase.grElevation->height();
	   pmElevation = QPixmap(wdElev, htElev);
	   // And at last for speed
	   wdSpeed = ui_sportwatcherWidgetBase.grSpeed->width() - 2;
	   htSpeed = ui_sportwatcherWidgetBase.grSpeed->height();
	   pmSpeed = QPixmap(wdSpeed, htSpeed);
	   // Initialize QPainter
	   ptHR.begin(&pmHR);
	   ptElevation.begin(&pmElevation);
	   ptSpeed.begin(&pmSpeed);
	}
	else if (ActivePrint && pw > 0 && ph > 0)
	{
	   wdHR = wdElev = wdSpeed = pw;
	   htHR = htElev = htSpeed = ph;
	   prHR = prElevation = prSpeed = QPixmap(pw, ph);
	   ptHR.begin(&prHR);
	   ptElevation.begin(&prElevation);
	   ptSpeed.begin(&prSpeed);
	}
	else
	   return;

	// we need a somewhat bigger area to draw our curves than
	// we have with the real min and max values.
	if (max_height > 0.0)
	{
	double add = (max_height - min_height) / 100.0 * 5.0;	// Percent

	   maxHeight = max_height + add;
	   minHeight = min_height - add;

	   if (minHeight < 0.0)		// make sure, we are not too deep
	      minHeight = 0.0;
	}
	else
	   maxHeight = minHeight = 0.0;

	if (max_speed > 0.0)
	{
	double add = (max_speed - min_speed) / 100.0 * 5.0;	// Percent

	   maxSpeed = max_speed + add;
	   minSpeed = min_speed - add;

	   if (minSpeed < 0.0)		// make sure, we are not too deep
	      minSpeed = 0.0;
	}
	else
	   maxSpeed = minSpeed = 0.0;

	if (max_hr > 0)
	{
	   maxHr = max_hr + 10;
	   minHr = min_hr - 10;

	   if (minHr < 0)
	      minHr = 0;
	}
	else
	   maxHr = minHr = 0;

	// Define colors
	QColor background(220, 220, 220);	// Background of graphic
	QColor mark(255, 255, 255);		// Lines inside curve area
	QColor hlight(180, 180, 180);		// area of current lap
	hlight.setAlpha(128);			// 50% transparent
	QColor frame(0, 0, 0);			// Text and borders
	QColor barcol(151, 190, 13);		// heart rate
	QColor barcol2(190, 151, 13);		// height over NN
	QColor red(220, 128, 128);		// speed
	QColor blue(0, 0, 240);
	QFont fntNormal("Helvetica");
//	QFont fntBold("Helvetica", 10, QFont::Bold);
	fntNormal.setPixelSize(10);
	fntNormal.setStyleHint(QFont::Helvetica);
//	fntBold.setPixelSize(10);
//	fntBold.setStyleHint(QFont::Helvetica);
	// Calculate ticks
	margin_left = 1;
	margin_right = 40;
	margin_bottom = 12;
	lineHeight = 10;
//	rh = height - margin_bottom - 1;

	// Calculate the ticks for width and height for every draw area
	for (i = 0; i < 3; i++)
	{
	   if (i == 2)	// Speed?
	   {
	      rhSpeed = htSpeed - margin_bottom - 1;
	      wtiSpeed = (double)(wdSpeed - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
	      htiSpeed = (double)rhSpeed / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
	   }
	   else if (i == 1)	// Elevation?
	   {
	      rhElev = htElev - margin_bottom - 1;
	      wtiElev = (double)(wdElev - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
	      htiElev = (double)rhElev / (maxHeight - minHeight);		// 1 tick = 1 meter
	   }
	   else			// heart rate
	   {
	      rhHR = htHR - margin_bottom - 1;
	      wtiHR = (double)(wdHR - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
	      htiHR = (double)rhHR / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
	   }
	}

	// Fill background with background colors
	ptHR.fillRect(0, 0, wdHR + 4, htHR + 4, background);
	ptElevation.fillRect(0, 0, wdElev + 4, htElev + 4, background);
	ptSpeed.fillRect(0, 0, wdSpeed + 4, htSpeed + 4, background);
	// Draw a grid with markers at every 10 minutes
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
	// Bottom border line
	x1 = margin_left;

	y1 = htHR - margin_bottom;
	x2 = wdHR - margin_right;
	y2 = y1;
	ptHR.drawLine(x1, y1, x2, y2);

	y1 = htElev - margin_bottom;
	x2 = wdElev - margin_right;
	y2 = y1;
	ptElevation.drawLine(x1, y1, x2, y2);

	y1 = htSpeed - margin_bottom;
	x2 = wdSpeed - margin_right;
	y2 = y1;
	ptSpeed.drawLine(x1, y1, x2, y2);

	// Left border line
	x1 = x2 = margin_left;
	y1 = 2;
	y2 = htHR - margin_bottom;
	ptHR.drawLine(x1, y1, x2, y2);
	
	y2 = htElev - margin_bottom;
	ptElevation.drawLine(x1, y1, x2, y2);
	
	y2 = htSpeed - margin_bottom;
	ptSpeed.drawLine(x1, y1, x2, y2);
	
	// right border line
	x1 = x2 = wdHR - margin_right;
	ptHR.drawLine(x1, y1, x2, y2);

	x1 = x2 = wdElev - margin_right;
	ptElevation.drawLine(x1, y1, x2, y2);

	x1 = x2 = wdSpeed - margin_right;
	ptSpeed.drawLine(x1, y1, x2, y2);

	// Draw some darker lines to show the laps, if we have one
	QDateTime *qt;
	QTime zeit = StartTime.time();
	rn = ds.getRunNode();
	ptHR.setPen(QPen(hlight, 1, Qt::SolidLine));
	ptElevation.setPen(QPen(hlight, 1, Qt::SolidLine));
	ptSpeed.setPen(QPen(hlight, 1, Qt::SolidLine));

	for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
	{
	   if ((lp = ds.getLap(i)) == NULL)
	      continue;

	   qt = garmin_dtime(lp->start_time);
	   secs = zeit.secsTo(qt->time());
	   delete qt;
	   // heart rate
	   x1 = secs * wtiHR + margin_left + 1;
	   ptHR.drawLine(x1, 2, x1, htHR - margin_bottom);
	   // Elevation
	   x1 = secs * wtiElev + margin_left + 1;
	   ptElevation.drawLine(x1, 2, x1, htElev - margin_bottom);
	   // Speed
	   x1 = secs * wtiSpeed + margin_left + 1;
	   ptSpeed.drawLine(x1, 2, x1, htSpeed - margin_bottom);
	}

	// Grid vertical
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
	ptHR.setFont(fntNormal);
	ptHR.drawText(margin_left, htHR - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
	
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
	ptElevation.setFont(fntNormal);
	ptElevation.drawText(margin_left, htElev - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
	
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
	ptSpeed.setFont(fntNormal);
	ptSpeed.drawText(margin_left, htSpeed - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
	
	ptHR.save();
	ptHR.rotate(270);
	ptHR.setPen(QPen(blue, 1, Qt::SolidLine));

	ptElevation.save();
	ptElevation.rotate(270);
	ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
	
	ptSpeed.save();
	ptSpeed.rotate(270);
	ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));

	// Information on right side
	ptHR.drawText((htHR + 4) * -1, wdHR - 1 - lineHeight, htHR - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
	ptElevation.drawText((htElev + 4) * -1, wdElev - 1 - lineHeight, htElev - 2, lineHeight, Qt::AlignCenter, i18n("Elevation (%1)").arg((Units == 1) ? "ft" : "m"));
	ptSpeed.drawText((htSpeed + 4) * -1, wdSpeed - 1 - lineHeight, htSpeed - 2, lineHeight, Qt::AlignCenter, i18n("Speed (%1)").arg((Units == 1) ? "mph" : "km/h"));

	// restore to normal
	ptHR.restore();
	ptHR.setPen(QPen(mark, 1, Qt::SolidLine));

	ptElevation.restore();
	ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));

	ptSpeed.restore();
	ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
	// Draw the time scale on the bottom of the graphic
	for (i = 0; (unsigned int)i < (max_time + ds.getPauseTime()); i++)
	{
	bool loop = false;

	   if (i > 0 && !(i % 600))	// every 10 minutes
	   {
	      for (int j = 0; j < 3; j++)
	      {
		 switch (j)
		 {
		    case 0: w_tick = wtiHR; width = wdHR; height = htHR; break;
		    case 1: w_tick = wtiElev; width = wdElev; height = htElev; break;
		    case 2: w_tick = wtiSpeed; width = wdSpeed; height = htSpeed; break;
		 }

		 x1 = x2 = margin_left + w_tick * i;

		 if (x1 == (width - margin_right - 25))
		 {
		    loop = true;
		    break;
		 }

		 y1 = 2;
		 y2 = height - margin_bottom;
		 QTime tm(0, 0, 0);
		 tm = tm.addSecs(i);

		 if (j == 0)
		 {
		    ptHR.drawLine(x1, y1, x2, y2);
		    ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
		    ptHR.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
		    ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
		 }
		 else if (j == 1)
		 {
		    ptElevation.drawLine(x1, y1, x2, y2);
		    ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
		    ptElevation.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
		    ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
		 }
		 else
		 {
		    ptSpeed.drawLine(x1, y1, x2, y2);
		    ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
		    ptSpeed.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
		    ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
		 }
	      }

	      if (loop)
	      {
		 loop = false;
		 continue;
	      }
	   }
	}

	// This is the total time, with pauses included, at the lower right
	// corner of the graphic.
	QTime tm(0, 0, 0);
	QString qs;
	tm = tm.addSecs(max_time + ds.getPauseTime());
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
	ptHR.drawText(wdHR - margin_right - 25, htHR - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));

	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
	ptElevation.drawText(wdElev - margin_right - 25, htElev - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));

	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
	ptSpeed.drawText(wdSpeed - margin_right - 25, htSpeed - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));

	// Draw the minimal elevation, speed and heart rate
	// Heart rate
	ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
	ptHR.drawText(wdHR - margin_right + 2, htHR - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
	// Elevation
	ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
	ptElevation.drawText(wdElev - margin_right + 2, htElev - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
	// Speed
	ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
	ptSpeed.drawText(wdSpeed - margin_right + 2, htSpeed - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", minSpeed));

	ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
	ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
	ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));

	// Grid horizontal
	int factor = 0;
	int target = 0;

	for (int j = 0; j < 3; j++)
	{
	   switch (j)
	   {
	      case 0: width = wdHR; height = htHR; w_tick = wtiHR; h_tick = htiHR; rh = rhHR;
		      factor = (maxHr - minHr) / (rhHR / 12);
		      target = (maxHr - minHr);
	      break;

	      case 1: width = wdElev; height = htElev; w_tick = wtiElev; h_tick = htiElev; rh = rhElev;
		      factor = (maxHeight - minHeight) / (rhElev / 12);
		      target = (int)(maxHeight - minHeight);
	      break;

	      case 2: width = wdSpeed; height = htSpeed; w_tick = wtiSpeed; h_tick = htiSpeed; rh = rhSpeed;
		      factor = (maxSpeed - minSpeed) / (rhSpeed / 12);
		      target = (int)(maxSpeed - minSpeed);
	      break;
	   }

	   // To prevent a division by zero error, we check the <factor>
	   if (factor == 0)
	      factor = 1;

	   // Beside the horizontal part of the grid, we draw the scale on the
	   // the right side too.
	   int oldy = height;

	   for (i = 0; i < target; i++)
	   {
	      if (i > 0 && !(i % factor))
	      {
		 x1 = margin_left + 1;
		 x2 = width - margin_right - 1;
		 y1 = y2 = rh - h_tick * i;

		 if (y1 < 12)
		    break;

		 switch (j)
		 {
		    case 0:
		       ptHR.drawLine(x1, y1, x2, y2);
		       ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
		       ptHR.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
		       ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
		    break;

		    case 1:
		       ptElevation.drawLine(x1, y1, x2, y2);
		       ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
		       ptElevation.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));
		       ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
		    break;

		    case 2:
		       ptSpeed.drawLine(x1, y1, x2, y2);
		       ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
		       ptSpeed.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", minSpeed + i));
		       ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
		    break;
		 }

		 oldy = y1;
	      }
	   }
	}

	// To make our graphics more beautiful, we draw lines for the
	// heart rate limits and the average heart rate.
	if (max_hr > 0)
	{
	int ay1, ay2, ay3, ay4, ay5;

	   x1 = margin_left + 1;
	   x2 = wdHR - margin_right - 1;

	   ay1 = (double)rh - (double)(lower1 - minHr) * htiHR;
	   ay2 = (double)rh - (double)(lower2 - minHr) * htiHR;
	   ay3 = (double)rh - (double)(lower3 - minHr) * htiHR;
	   ay4 = (double)rh - (double)(upper3 - minHr) * htiHR;
	   ay5 = (double)rh - (double)(avg_hr - minHr) * htiHR;

	   ptHR.setPen(QPen(barcol2, 1, Qt::DashLine));	// color for limits

	   if (lower1 > minHr && lower1 < maxHr)
	      ptHR.drawLine(x1, ay1, x2, ay1);

	   if (lower2 > minHr && lower2 < maxHr)
	      ptHR.drawLine(x1, ay2, x2, ay2);

	   if (lower3 > minHr && lower3 < maxHr)
	      ptHR.drawLine(x1, ay3, x2, ay3);

	   if (upper3 > minHr && upper3 < maxHr)
	      ptHR.drawLine(x1, ay4, x2, ay4);

	   ptHR.setPen(QPen(red, 1, Qt::DashDotLine));	// color for average heart rate

	   if (avg_hr > minHr && avg_hr < maxHr)
	      ptHR.drawLine(x1, ay5, x2, ay5);
	}

	// Now we have a grid and we've done the scaling.
	// This is the point where we draw the curves itself.
	// We use different colors to draw the lines:
	//
	// Green: Elevation
	// Red:   Speed
	// Blue   Heart Rate
	//
	x1 = x2 = y1 = y2 = 0;
	int hy1, hy2, hx1, hx2;
	int sy1, sy2, sx1, sx2;
	hy1 = hy2 = hx1 = hx2 = 0;
	sy1 = sy2 = sx1 = sx2 = 0;
	int hEc = 0;
	i = 0;
	AVGHEIGHT *avgHakt, *avgHfirst, *avgHlast, *avgHeight = 0;
	avgHfirst = avgHlast = 0;
	// To even the surface lines, we store every altitude in the
	// memory, by building a chain. Then, if the user has set in the
	// settings (Contour == true), we will even the line by calculating
	// the average of 10 messure points and setting every value to the
	// average, who is up or down more than 2 meters from the average.
	while ((point = ds.getPoint(i)) != 0)
	{
	   if (point->alt > 20000.0 || point->alt < -1000.0)
	   {
	      i++;
	      continue;
	   }

	   if (!avgHeight)
	   {
	      avgHeight = new AVGHEIGHT;
	      avgHeight->alt = point->alt;
	      avgHeight->pos = hEc;
	      avgHeight->prev = 0;
	      avgHeight->next = 0;
	      avgHakt = avgHeight;
	      avgHfirst = avgHeight;
	   }
	   else
	   {
	      avgHakt = new AVGHEIGHT;
	      avgHakt->alt = point->alt;
	      avgHakt->pos = hEc;
	      avgHakt->next = 0;
	      avgHakt->prev = avgHeight;
	      avgHeight->next = avgHakt;
	      avgHeight = avgHakt;
	   }

	   // FIXME: Currently we can not draw below 0 meters, because the
	   // base line is always 0!
	   if (avgHakt->alt < minHeight)
	      avgHakt->alt = minHeight;

	   hEc++;
	   i++;
	}

	avgHlast = avgHeight;
	// If wanted, even the lines
	if (Contour && hEc > 0)
	{
	double alt[100], avg, avg1, avg2, avg3, avg4;
	int a, pos;

	   for (i = 0; i < (hEc + 100); i += 100)
	   {
	      avg = avg1 = avg2 = avg3 = avg4 = 0.0;
	      pos = 0;

	      for (a = 0; a < 100; a++)
	      {
		 alt[a] = getAvgAlt(avgHfirst, i + a);
		 avg += alt[a];

		 if (a < 25)
		    avg1 += alt[a];
		 else if (a < 50)
		    avg2 += alt[a];
		 else if (a < 75)
		    avg3 += alt[a];
		 else
		    avg4 += alt[a];
	      }

	      if ((i + 100) >= hEc)
		 avg /= (double)(hEc - i) + 1.0;
	      else
		 avg /= 100.0;

	      for (a = 0; a < 100; a++)
	      {
		 if ((avgHakt = getAvgPtr(avgHfirst, i + a)) != 0)
		 {
		    if ((avgHakt->alt - avg) > 2 || (avgHakt->alt - avg) < -2)
		       avgHakt->alt = avg;
		 }
	      }
	   }
	}

	// plot the elevation, speed and/or heart rate. Depends on <cuType>)
	i = 0;
	int j = 0;
	POINT *oldPoint = 0;
	double speed = 0.0;	// calculated speed
	bool pause = false;	// filter pause out of speed
	unsigned long t1, t2;
	t1 = t2 = 0;
	
	while ((point = ds.getPoint(i)) != 0)
	{
	bool loop = false;

	   if (!oldPoint)
	      oldPoint = point;

	   // calculate the y position based on the time
	   qt = garmin_dtime(point->time);
	   secs = zeit.secsTo(qt->time());
	   delete qt;

	   for (int c = 0; c < 3; c++)
	   {
	      if (c == 0)
	      {
		 x2 = secs * wtiHR + margin_left + 1;
		 hx2 = x2;
		 sx2 = x2;

		 if (x1 == 0)
		    x1 = x2;

		 if (hx1 == 0)
		    hx1 = hx2;

		 if (sx1 == 0)
		    sx1 = sx2;
	      }
	      else if (c == 1)
	      {
		 x2 = secs * wtiElev + margin_left + 1;
		 hx2 = x2;
		 sx2 = x2;

		 if (x1 == 0)
		    x1 = x2;

		 if (hx1 == 0)
		    hx1 = hx2;

		 if (sx1 == 0)
		    sx1 = sx2;
	      }
	      else
	      {
		 x2 = secs * wtiSpeed + margin_left + 1;
		 hx2 = x2;
		 sx2 = x2;

		 if (x1 == 0)
		    x1 = x2;

		 if (hx1 == 0)
		    hx1 = hx2;

		 if (sx1 == 0)
		    sx1 = sx2;
	      }
	      // The speed is not very exact, because smallest time is
	      // one second. This allows a maximum error of 99 hundredths
	      // of a second, what is very close to one second. Because of
	      // this, speed seems to hop for every messure point. This
	      // looks ugly, but currently I don't know how to make it
	      // better.
	      if (c == 2)	// Draw speed?
	      {
	      double dist;
	      double sc;

		 if (!pause && point->distance > 1.0e10)
		 {
		    pause = true;
		    t1 = point->time;
		 }
		 else if (pause)
		 {
		    pause = false;
		    t2 = point->time;
		    i += 2;
		    loop = true;
		 }

		 if (point->distance >= 0.1 && point->distance < 1.0e10)
		 {
		    dist = point->distance - oldPoint->distance;
		    sc = point->time - oldPoint->time;
		    LAP *runde = ds.getLapT (point->time);

		    if (t2 > t1)
		    {
		       sc -= t2 - t1;

		       if (sc <= 0.0)
			  sc = 1.0;		// at least 1 second!
		    }

		    speed = (dist / sc) * 3.6;

		    if (runde && runde->max_speed > 0.0 && speed > (runde->max_speed * 3.6))
		       speed = runde->max_speed * 3.6;

		    if (Units == 1)
		       speed /= 1.609344;

		    if (speed < minSpeed || speed > 400.0)
		       speed = minSpeed;

		    y2 = (double)rh - (speed - minSpeed) * htiSpeed;

		    if (y1 == 0)
		       y1 = y2;

		    ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
		    ptSpeed.drawLine(x1, y1, x2, y2);
		    y1 = y2;
		    x1 = x2;
		    t1 = t2 = 0;
		    oldPoint = point;
		 }
	      }

	      if (c == 1)		// Draw elevation?
	      {
		 if (point->alt < 20000.0 && point->alt > -1000.0)
		 {
		 double alt = getAvgAlt(avgHfirst, j);

		    j++;

		    sy2 = (double)rh - (alt - minHeight) * htiElev;

		    if (sy1 == 0)
		       sy1 = sy2;

		    ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
		    ptElevation.drawLine(sx1, sy1, sx2, sy2);
		    sy1 = sy2;
		    sx1 = sx2;
		 }
	      }

	      if (point->heart_rate > 0 && c == 0)	// Draw heart rate?
	      {
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * htiHR;

		 if (hy1 == 0)
		    hy1 = hy2;

		 ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
		 ptHR.drawLine(hx1, hy1, hx2, hy2);
		 hy1 = hy2;
		 hx1 = hx2;
	      }
	   }

	   if (loop)
	   {
	      loop = false;
	      continue;
	   }

	   i++;
	}

	ptHR.end();
	ptElevation.end();
	ptSpeed.end();

	if (!ActivePrint)
	{
	   ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
	   ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
	   ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
	}
	// free the chain of altitudes
	avgHakt = avgHfirst;

	while (avgHakt)
	{
	   avgHeight = avgHakt->next;
	   delete avgHakt;
	   avgHakt = avgHeight;
	}

	DIRTY = false;
}

double sportwatcherWidget::getAvgAlt(AVGHEIGHT *avgHeight, int pos)
{
AVGHEIGHT *akt;

	if (!avgHeight)
	   return 0.0;

	akt = avgHeight;

	while (akt)
	{
	   if (akt->pos == pos)
	      return akt->alt;

	   akt = akt->next;
	}

	return 0.0;
}

AVGHEIGHT *sportwatcherWidget::getAvgPtr(AVGHEIGHT *avgHeight, int pos)
{
AVGHEIGHT *akt;

	akt = avgHeight;

	while (akt)
	{
	   if (akt->pos == pos)
	      return akt;

	   akt = akt->next;
	}

	return 0;
}

void sportwatcherWidget::resizeEvent(QResizeEvent *e)
{
	if (e->size() != e->oldSize())
	   DIRTY=true;

	showTrack(zfactor);
	showCurves();
	tabDirt2 = true;

	if (curTab == 2)
	{
	   showThreeCurve();
	   tabDirt2 = false;
	}

	DIRTY=false;
}

void sportwatcherWidget::mouseMoveEvent(QMouseEvent *e)
{
QPoint pos(0, 0);
QPoint ev = ui_sportwatcherWidgetBase.imgMap->mapFrom(this, e->pos());
static QRect coord;

	DIRTY=true;

	if (curTab == 0)
	{
	   if (!stateHand)
	      return;

	   if (ev.x() >= pos.x() &&
	       ev.y() >= pos.y() &&
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.imgMap->geometry().width()) &&
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.imgMap->geometry().height()))
	   {
	      if (lmbPressed == 1)
	      {
		 coord.setCoords(ev.x(), ev.y(), 0, 0);
		 lmbPressed = 0;
	      }
	      else
	      {
		 coord.setRight(ev.x());
		 coord.setBottom(ev.y());
	      }

	      if (lmbPressed == 2)
	      {
		 showTrack(zfactor, coord, mapLap);
		 lmbPressed = 0;
	      }
	   }
	}

	if (curTab == 2)
	{
	   // look in which of the three QLabels the mouse is, if it is in
	   // one of them.
	   ev = ui_sportwatcherWidgetBase.grHR->mapFrom(this, e->pos());

	   if (ev.x() >= pos.x() &&
	       ev.y() >= pos.y() &&
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grHR->geometry().width()) &&
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grHR->geometry().height()))
	   {
	      drawGrHR (ev.x(), ev.y());
	      drawGrElev (ev.x(), -1);
	      drawGrSpeed (ev.x(), -1);
	   }

	   ev = ui_sportwatcherWidgetBase.grElevation->mapFrom(this, e->pos());

	   if (ev.x() >= pos.x() &&
	       ev.y() >= pos.y() &&
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grElevation->geometry().width()) &&
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grElevation->geometry().height()))
	   {
	      drawGrHR (ev.x(), -1);
	      drawGrElev (ev.x(), ev.y());
	      drawGrSpeed (ev.x(), -1);
	   }

	   ev = ui_sportwatcherWidgetBase.grSpeed->mapFrom(this, e->pos());

	   if (ev.x() >= pos.x() &&
	       ev.y() >= pos.y() &&
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grSpeed->geometry().width()) &&
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grSpeed->geometry().height()))
	   {
	      drawGrHR (ev.x(), -1);
	      drawGrElev (ev.x(), -1);
	      drawGrSpeed (ev.x(), ev.y());
	   }
	}

	DIRTY=false;
}

void sportwatcherWidget::mousePressEvent(QMouseEvent *e)
{
	if (stateHand && e->button() == Qt::LeftButton)
	   lmbPressed = 1;	// Left Mouse Button is pressed
	else if (stateHand)
	   lmbPressed = 0;	// Wrong button is pressed

	if (stateGlas)
	{
	   if (e->button() == Qt::LeftButton)
	      btGlasPlusSlot();
	   else if (e->button() == Qt::RightButton)
	      btGlasMinusSlot();
	}
}

void sportwatcherWidget::mouseReleaseEvent(QMouseEvent *e)
{
	if (stateHand && e->button() == Qt::LeftButton)
	{
	   lmbPressed = 2;	// Left Mouse Button was released
	   mouseMoveEvent(e);
	}
}

/*
 * Private functions to draw cross and/or a bar to reflect the mouse
 * pointer on tab 2.
 */
void sportwatcherWidget::drawGrHR (int x, int y)
{
int width, height;
QPixmap pm = pmHR.copy(pmHR.rect());
QPainter paint;

	width = ui_sportwatcherWidgetBase.grHR->width();
	height = ui_sportwatcherWidgetBase.grHR->height();
	ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);

	if (x > (width - 40) || y > (height - 12) || x < 1)
	{
	   ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
	   ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
	   return;
	}

	if (tabDirt2)
	{
	   DIRTY = true;
	   showThreeCurve ();
	   tabDirt2 = false;
	   DIRTY = false;
	}

	paint.begin(&pm);
	QColor black(0, 0, 0);
	black.setAlpha (128);
	paint.setPen(QPen(black, 1, Qt::SolidLine));
	// horizontal line, if y != -1
	if (y >= 0)
	   paint.drawLine(2, y, width - 41, y);

	// vertical line
	paint.drawLine(x, 1, x, height - 13);
	paint.end();
	ui_sportwatcherWidgetBase.grHR->setPixmap(pm);
}

void sportwatcherWidget::drawGrElev (int x, int y)
{
int width, height;
QPixmap pm = pmElevation.copy(pmElevation.rect());
QPainter paint;

	width = ui_sportwatcherWidgetBase.grElevation->width();
	height = ui_sportwatcherWidgetBase.grElevation->height();
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);

	if (x > (width - 40) || y > (height - 12) || x < 1)
	{
	   ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
	   ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
	   return;
	}

	if (tabDirt2)
	{
	   DIRTY = true;
	   showThreeCurve ();
	   tabDirt2 = false;
	   DIRTY = false;
	}

	paint.begin(&pm);
	QColor black(0, 0, 0);
	black.setAlpha (128);
	paint.setPen(QPen(black, 1, Qt::SolidLine));
	// horizontal line, if y != -1
	if (y >= 0)
	   paint.drawLine(2, y, width - 41, y);

	// vertical line
	paint.drawLine(x, 1, x, height - 13);
	paint.end();
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pm);
}

void sportwatcherWidget::drawGrSpeed (int x, int y)
{
int width, height;
QPixmap pm = pmSpeed.copy(pmSpeed.rect());
QPainter paint;

	width = ui_sportwatcherWidgetBase.grSpeed->width();
	height = ui_sportwatcherWidgetBase.grSpeed->height();
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);

	if (x > (width - 40) || y > (height - 12) || x < 1)
	{
	   ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
	   ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
	   return;
	}

	if (tabDirt2)
	{
	   DIRTY = true;
	   showThreeCurve ();
	   tabDirt2 = false;
	   DIRTY = false;
	}

	paint.begin(&pm);
	QColor black(0, 0, 0);
	black.setAlpha (128);
	paint.setPen(QPen(black, 1, Qt::SolidLine));
	// horizontal line, if y != -1
	if (y >= 0)
	   paint.drawLine(2, y, width - 41, y);

	// vertical line
	paint.drawLine(x, 1, x, height - 13);
	paint.end();
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pm);
}

/*
 * Private functions to help decode the data
 */
QDateTime *sportwatcherWidget::garmin_dtime (uint32 t)
{
time_t     tval;
struct tm  tmval;
QTime ti;
QDate dt;
QDateTime *qt;

	if (t == 0)
	   return new QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0));

	tval = t + TIME_OFFSET;
	localtime_r (&tval, &tmval);
	qt = new QDateTime();
	qt->setDate(QDate(tmval.tm_year+1900, tmval.tm_mon+1, tmval.tm_mday));
	qt->setTime(QTime(tmval.tm_hour, tmval.tm_min, tmval.tm_sec, 0));
	/* OK.  Done. */
	return qt;
}

bool sportwatcherWidget::writeTag(const QFile &fn, const QString &str, int indent)
{
QString qs;
char *p;
//QCString qcs;
int i;

	if (indent > 0)
	   qs.fill(' ', indent * 3);

	qs.append(str);
//	qcs = qs.utf8();
//	qstrcpy(p, qcs);
	p = strdup (qs.toUtf8().data());
	i = strlen(p);

	if (write(fn.handle(), p, i) != i)
	{
	   free (p);
	   return false;
	}

	free (p);
	return true;
}

#if defined HAVE_GDAL
bool sportwatcherWidget::writeWMSTag(double llat, double llon, double rlat, double rlon, int width, int height)
{
QFile fl(MAP);
QString xml, s, srs, crs, styles, bSize, ext;
QDir dir = QDir::home();
QString path = dir.absolutePath();
int item, isrs;
double _llat, _llon, _rlat, _rlon;
bool offline, square;

	if (!fl.open(QIODevice::ReadWrite | QIODevice::Truncate))
	{
	   KMessageBox::error (this, i18n("Error opening or creating the WMS tag file!\nPlease check file name and/or permissions."));
	   return false;
	}

	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
	KConfigGroup wms (&cfg, "WMS");
	square = wms.readEntry("Square", false);
	styles = wms.readEntry("Styles", QString(""));

	xml = "<GDAL_WMS>\n";
	xml += "   <Service name=\"WMS\">\n";
	xml += "      <Version>1.1.1</Version>\n";
	xml += "      <ServerURL>" + wms.readEntry("ServerURL", "http://onearth.jpl.nasa.gov/wms.cgi") + "?</serverURL>\n";
	isrs = wms.readEntry("SRS", 0);
	_llon = llon;
	_llat = llat;
	_rlon = rlon;
	_rlat = rlat;
	offline = false;

	switch (isrs)
	{
	   case 0: srs = QString("EPSG:4326"); break;

	   case 1:
	      srs = QString("EPSG:31257");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31257, width, height, square);
	   break;

	   case 2:
	      srs = QString("EPSG:31258");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31258, width, height, square);
	   break;

	   case 3:
	      srs = QString("EPSG:31259");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31259, width, height, square);
	   break;

	   case 4:
	      srs = QString("EPSG:31286");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31286, width, height, square);
	   break;

	   case 5:
	      srs = QString("EPSG:31287");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31287, width, height, square);
	   break;

	   case 6:
	      srs = QString("EPSG:31288");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31288, width, height, square);
	   break;

	   case 7:
	      srs = QString("EPSG:900913");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 900913, width, height, square);
	   break;

	   case 8:
	      srs = QString("EPSG:3395");
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 3395, width, height, square);
	   break;

	   default: srs = QString("EPSG:4326");
	}

	xml += "      <SRS>" + srs + "</SRS>\n";
	item = wms.readEntry("CRS", 0);

	switch (item)
	{
	   case 0: crs = QString("CRS:83"); break;
	   case 1: crs = QString("CRS:84"); break;
	   case 2: crs = QString("EPSG:4326"); break;
	   case 3: crs = QString("EPSG:31259"); break;
	   case 4: crs = QString("EPSG:31287"); break;
	   case 5: crs = QString("EPSG:900913"); break;
	   case 6: crs = QString("EPSG:3395"); break;
	   default: crs = QString("CRS:84"); break;
	}

	xml += "      <CRS>" + crs + "</CRS>\n";
	item = wms.readEntry("Image", 2);
	xml += "      <ImageFormat>image/";

	switch (item)
	{
	   case 0: xml += "gif"; ext = QString(".gif"); break;
	   case 1: xml += "jpeg"; ext = QString(".jpg"); break;
	   case 2: xml += "png"; ext = QString(".png"); break;
	   case 3: xml += "tiff"; ext = QString(".tif"); break;
	   default: xml += "png"; ext = QString(".png");
	}

	xml += "</ImageFormat>\n";

	xml += "      <Layers>" + wms.readEntry("Layer", QString("")) + "</Layers>\n";

	if (!styles.isEmpty())
	   xml += "      <Styles>" + styles + "</Styles>\n";

	xml += "      <BBoxOrder>xyXY</BBoxOrder>\n";
	xml += "   </Service>\n";
	xml += "   <DataWindow>\n";
	s.sprintf ("%f", _llat);
	xml += "      <UpperLeftX>" + s + "</UpperLeftX>\n";
	s.sprintf ("%f", _llon);
	xml += "      <UpperLeftY>" + s + "</UpperLeftY>\n";
	s.sprintf ("%f", _rlat);
	xml += "      <LowerRightX>" + s + "</LowerRightX>\n";
	s.sprintf ("%f", _rlon);
	xml += "      <LowerRightY>" + s + "</LowerRightY>\n";
	s.sprintf ("%d", width);
	xml += "      <SizeX>" + s + "</SizeX>\n";
	s.sprintf ("%d", height);
	xml += "      <SizeY>" + s + "</SizeY>\n";
	xml += "   </DataWindow>\n";

/*	switch (isrs)
	{
	   case 0: srs = QString("EPSG:4326"); break;
	   case 1: srs = QString("EPSG:31259"); break;
	   case 2: srs = QString("EPSG:31286"); break;
	   case 3: srs = QString("EPSG:31287"); break;
	   case 4: srs = QString("EPSG:31288"); break;
	   default: srs = QString("EPSG:4326");
	}
*/
//	srs = QString("EPSG:4326");
	xml += "   <Projection>" + crs + "</Projection>\n";
	xml += "   <BandsCount>" + wms.readEntry("Bands", QString("3")) + "</BandsCount>\n";
	item = wms.readEntry("Tile", 2);

	switch (item)
	{
	   case 0: bSize = QString("64"); break;
	   case 1: bSize = QString("128"); break;
	   case 2: bSize = QString("256"); break;
	   case 3: bSize = QString("512"); break;
	   case 4: bSize = QString("1024"); break;
	   default: bSize = QString("256");
	}

	xml += "   <BlockSizeX>" + bSize + "</BlockSizeX>\n";
	xml += "   <BlockSizeY>" + bSize + "</BlockSizeY>\n";
	xml += "   <OverviewCount>" + wms.readEntry("Overview", QString("10")) + "</OverviewCount>\n";
	xml += "   <Cache>\n";
	xml += "      <Path>" + path + "/.gdalwmscache" + "</Path>\n";
	xml += "      <Depth>" + wms.readEntry("Depth", QString("2")) + "</Depth>\n";
	xml += "      <Extension>" + ext + "</Extension>\n";
	xml += "   </Cache>\n";
	QString off((wms.readEntry("Offline", false)) ? "true" : "false");
	QString adv((wms.readEntry("Advice", false)) ? "true" : "false");
	QString ver((wms.readEntry("Verify", true)) ? "true" : "false");

	if (offline)
	   xml += "   <OfflineMode>true</OfflineMode>\n";
	else
	   xml += "   <OfflineMode>" + off + "</OfflineMode>\n";

	xml += "   <AdviseRead>" + adv + "</AdviseRead>\n";
	xml += "   <VerifyAdviseRead>" + ver + "</VerifyAdviseRead>\n";
	xml += "</GDAL_WMS>\n";

	write (fl.handle(), xml.toAscii().data(), strlen (xml.toAscii().data()));
	fl.close();
	return true;
}

bool sportwatcherWidget::transCoords (double *x1, double *y1, double *x2, double *y2, int code, int width, int height, bool square)
{
OGRSpatialReference oSourceSRS, oTargetSRS;
OGRCoordinateTransformation *poCT;

	oSourceSRS.SetWellKnownGeogCS ("WGS84");
	oTargetSRS.importFromEPSG(code);
	poCT = OGRCreateCoordinateTransformation (&oSourceSRS, &oTargetSRS);

	if (poCT == NULL || !poCT->Transform( 1, x1, y1))
	{
	   KMessageBox::error(this, i18n("Translation between coordinate systems failed!"));

	   if (poCT != NULL)
	      delete poCT;

	   return true;
	}

	if (poCT != NULL)
	{
	   poCT->Transform (1, x2, y2);
	   delete poCT;
	}

	if (square)
	{
	double wdiff = (double)((long)(*x1 - *x2) / (width / 2 * 2) * (width / 2 * 2));
	double hdiff = (double)((long)(*y2 - *y1) / (height / 2 * 2) * (height / 2 * 2));

	   *x2 = *x1 - wdiff; // * (double)mFactor;
	   *y2 = *y1 + hdiff; // * (double)mFactor;
//	   *x2 = *x1 - (double)((long)(wdiff / mFactor * mFactor));
//	   *y2 = *y1 - (double)((long)(hdiff / mFactor * mFactor));
/*	   wdiff = wdiff - (*x1 - *x2);
	   hdiff = hdiff - (*y2 - *y1);
	   *x1 -= wdiff / 2.0;
	   *x2 -= wdiff / 2.0;
	   *y1 += hdiff / 2.0;
	   *y2 += hdiff / 2.0; */
	}

	return false;
}

QString *sportwatcherWidget::getProjection (int isrs, QString *srs)
{
	switch (isrs)
	{
	   case 0: *srs = QString("EPSG:4326"); break;
	   case 1: *srs = QString("EPSG:31257"); break;
	   case 2: *srs = QString("EPSG:31258"); break;
	   case 3: *srs = QString("EPSG:31259"); break;
	   case 4: *srs = QString("EPSG:31286"); break;
	   case 5: *srs = QString("EPSG:31287"); break;
	   case 6: *srs = QString("EPSG:31288"); break;
	   default: *srs = QString("EPSG:4326");
	}

	return srs;
}

bool sportwatcherWidget::warpImage(QString fn, QString *fName)
{
GDALDatasetH  hSrcDS, hDstDS;
GDALDataset *inSet, *outSet;
GDALDataType eDT;
GDALRasterBand *poBand;
GDALDriverH hDriver;
char hv0[256];
int nXSize, nYSize;
double adfGeoTransform[6];
double oriLeftLon, oriRightLon, oriLeftLat, oriRightLat;


	// Loading the user set geo coords of our source image and
	// load the projection used for that image
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
	KConfigGroup ic (&cfg, "ImageCoords");
	oriLeftLon = ic.readEntry("LeftLon", 0.0);
	oriLeftLat = ic.readEntry("LeftLat", 0.0);
	oriRightLon = ic.readEntry("RightLon", 0.0);
	oriRightLat = ic.readEntry("RightLat", 0.0);
	int isrs = ic.readEntry("SRS", 0);

	// Create a temporary file name for our output file
	strcpy (hv0, "/tmp/SportWatcherTIFFXXXXXX");
	mkdtemp (&hv0[0]);
	*fName = QString(hv0) + ".tif";

	// Open input and output files.
	if ((hSrcDS = GDALOpen(fn.toAscii().data(), GA_ReadOnly)) == NULL)
	{
	   KMessageBox::error(this, i18n("Error opening an image file!"));
	   return false;
	}

	inSet = (GDALDataset *)hSrcDS;
	// Create output with same datatype as first input band.
	poBand = inSet->GetRasterBand (1);
	eDT = poBand->GetRasterDataType ();

	if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
	{
	   KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
	   GDALClose (hSrcDS);
	   return false;
	}

	// Get dimensions of image and set transform data
	int nRasterCount = inSet->GetRasterCount();
	nXSize = inSet->GetRasterXSize();
	nYSize = inSet->GetRasterYSize();

	// cut the wanted region out of image
	transform *tf = new transform (oriLeftLat, oriLeftLon, oriRightLat, oriRightLon);
	tf->setDimensions(nXSize, nYSize);

	if (!tf->cutImage (geoRect.llat, geoRect.llon, geoRect.rlat, geoRect.rlon, fn))
	{
	   GDALClose (hSrcDS);
	   return false;
	}

	GDALClose (hSrcDS);
	QString nfn = fn + "_tmp.png";

	// repeat the part above and open the now cutted part of the image.
	// Open input and output files.
	if ((hSrcDS = GDALOpen(nfn.toAscii().data(), GA_ReadOnly)) == NULL)
	{
	   KMessageBox::error(this, i18n("Error opening an image file!"));
	   return false;
	}

	inSet = (GDALDataset *)hSrcDS;
	// Create output with same datatype as first input band.
	poBand = inSet->GetRasterBand (1);
	eDT = poBand->GetRasterDataType ();

	if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
	{
	   KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
	   GDALClose (hSrcDS);
	   return false;
	}

	// Get dimensions of image and set transform data
	nRasterCount = inSet->GetRasterCount();
	nXSize = inSet->GetRasterXSize();
	nYSize = inSet->GetRasterYSize();

	// Set the values needed to transform the image
	OGRSpatialReference iSRS;
	const char *iWKT;
	
	switch (isrs)
	{
	   case 0: iSRS.importFromEPSG(4326); break;
	   case 1: iSRS.importFromEPSG(31257); break;
	   case 2: iSRS.importFromEPSG(31258); break;
	   case 3: iSRS.importFromEPSG(31259); break;
	   case 4: iSRS.importFromEPSG(31286); break;
	   case 5: iSRS.importFromEPSG(31287); break;
	   case 6: iSRS.importFromEPSG(31288); break;
	   default: iSRS.importFromEPSG(4326);
	}

	iSRS.exportToWkt ((char **)&iWKT);

	if (inSet->SetProjection (iWKT) != CE_None)
	{
	   KMessageBox::error(this, i18n("Error setting projection on source!"));
	   GDALClose (hSrcDS);
	   return false;
	}

	adfGeoTransform[0] = geoRect.llon;
	adfGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)nXSize;
	adfGeoTransform[2] = 0.0;
	adfGeoTransform[3] = geoRect.llat;
	adfGeoTransform[4] = 0.0;
	adfGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)nYSize);

	if (inSet->SetGeoTransform (&adfGeoTransform[0]) != CE_None)
	{
	   KMessageBox::error(this, i18n("Error setting geo transform data to source!"));
	   GDALClose (hSrcDS);
	   return false;
	}

	// Get Source coordinate system.
	const char *pszSrcWKT, *pszDstWKT = NULL;

	if ((pszSrcWKT = GDALGetProjectionRef (hSrcDS)) == NULL)
	{
	   KMessageBox::error(this, i18n("Error getting the projection reference"));
	   GDALClose (hSrcDS);
	   return false;
	}

	// Setup output coordinate system that is UTM ? WGS84.
	OGRSpatialReference oSRS;

//	oSRS.SetUTM( 0, TRUE );
	oSRS.SetWellKnownGeogCS("WGS84");
	oSRS.exportToWkt ((char **)&pszDstWKT);

	// Create the output file.
	double adfDstGeoTransform[6];
	adfDstGeoTransform[0] = geoRect.llon;
	adfDstGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)geoRect.width;
	adfDstGeoTransform[2] = 0.0;
	adfDstGeoTransform[3] = geoRect.llat;
	adfDstGeoTransform[4] = 0.0;
	adfDstGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)geoRect.height);

	if ((hDstDS = GDALCreate(hDriver, fName->toAscii().data(), geoRect.width, geoRect.height,
			nRasterCount, eDT, NULL )) == NULL)
	{
	   KMessageBox::error(this, i18n("Error creating a temporary image file! (%1)").arg(*fName));
	   GDALClose (hSrcDS);
	   return false;
	}

	outSet = (GDALDataset *)hDstDS;

	for (int i = 0; i < nRasterCount; i++)
	{
	   poBand = outSet->GetRasterBand (i+1);
	   poBand->Fill (0.0);
	}

	if (outSet->SetProjection (pszDstWKT) != CE_None)
	{
	   KMessageBox::error(this, i18n("Error setting projection on destination!"));
	   GDALClose (hDstDS);
	   GDALClose (hSrcDS);
	   unlink (fName->toAscii().data());
	   return false;
	}

	if (outSet->SetGeoTransform (&adfDstGeoTransform[0]) != CE_None)
	{
	   KMessageBox::error(this, i18n("Error setting geo transform data to destination!"));
	   GDALClose (hDstDS);
	   GDALClose (hSrcDS);
	   unlink (fName->toAscii().data());
	   return false;
	}

	// Copy the color table, if required.
	GDALColorTableH hCT;

	for (int i = 0; i < nRasterCount; i++)
	{
	   hCT = GDALGetRasterColorTable (inSet->GetRasterBand (i+1));

	   if (hCT != NULL)
              GDALSetRasterColorTable (outSet->GetRasterBand (i+1), hCT);
	}

	// Setup warp options.
	GDALWarpOptions *psWarpOptions = GDALCreateWarpOptions();

	psWarpOptions->hSrcDS = hSrcDS;
	psWarpOptions->hDstDS = hDstDS;

	psWarpOptions->nBandCount = nRasterCount;
	psWarpOptions->panSrcBands =
        	(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );
	psWarpOptions->panDstBands =
		(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );

	for (int i = 0; i < nRasterCount; i++)
	{
	   psWarpOptions->panSrcBands[i] = i+1;
	   psWarpOptions->panDstBands[i] = i+1;
	}

//	psWarpOptions->pfnProgress = GDALTermProgress;

	// Establish reprojection transformer.
	psWarpOptions->pTransformerArg =
        	GDALCreateGenImgProjTransformer(hSrcDS,
					 GDALGetProjectionRef(hSrcDS), 
					 hDstDS,
					 GDALGetProjectionRef(hDstDS),
					 FALSE, 0.0, 1);

	psWarpOptions->pfnTransformer = GDALGenImgProjTransform;

	// Initialize and execute the warp operation.
	GDALWarpOperation oOperation;

	if (oOperation.Initialize (psWarpOptions) != CE_None)
	{
	   KMessageBox::error(this, i18n("Error initializing warp operation!"));
	   GDALClose (hDstDS);
	   GDALClose (hSrcDS);
	   unlink (fName->toAscii().data());
	   return false;
	}

	oOperation.ChunkAndWarpMulti (0, 0, geoRect.width, geoRect.height);
	GDALDestroyGenImgProjTransformer (psWarpOptions->pTransformerArg);
	GDALDestroyWarpOptions(psWarpOptions);

	GDALClose (hDstDS);
	GDALClose (hSrcDS);
	unlink (nfn.toAscii().data());
	return true;
}

#endif

#ifdef HAVE_GDAL
void spwErrorHandler(CPLErr err, int num, const char *msg)
{
ERRMSG *akt;
char et[32], hv0[4096];

	if (err == CE_None || !msg)
	   return;

	if (err == CE_Fatal)
	   std::cerr << "ERROR " << num << ": " << msg << endl;

	if (!firstError)
	{
	   firstError = new ERRMSG;
	   memset (firstError, 0, sizeof(ERRMSG));
	   akt = firstError;
	}
	else
	{
	   akt = firstError;

	   while (akt)
	   {
	      if (!akt->next)
		 break;

	      akt = akt->next;
	   }

	   akt->next = new ERRMSG;
	   akt = akt->next;
	   memset (akt, 0, sizeof(ERRMSG));
	}

	switch (err)
	{
	   case CE_None: strcpy (et, "None"); break;
	   case CE_Debug: strcpy (et, "DEBUG"); break;
	   case CE_Warning: strcpy (et, "Warning"); break;
	   case CE_Failure: strcpy (et, "Failure"); break;
	   case CE_Fatal: strcpy (et, "Fatal"); break;
	}

	memset(hv0, 0, sizeof(hv0));
	strncpy (hv0, msg, 4000);
	sprintf (akt->msg, "ERROR TYPE %s - %d: %s", et, num, hv0);
}

void destroyErrors()
{
ERRMSG *akt;

	if (!firstError)
	   return;

	akt = firstError;

	while (akt)
	{
	   firstError = akt->next;
	   delete akt;
	   akt = firstError;
	}

	firstError = 0;
}

QString catGDALError()
{
QString err;
ERRMSG *akt = firstError;

	while (akt)
	{
	   err += QString(akt->msg) + "\n";
	   akt = akt->next;
	}

	destroyErrors();
	return err;
}
#endif

#include "sportwatcherwidget.moc"

